diff --git a/_albion.sql b/_albion.sql index a35baa7..2e56925 100644 --- a/_albion.sql +++ b/_albion.sql @@ -32,50 +32,40 @@ create table _albion.metadata( snap_distance real not null default 1, precision real default .01, interpolation interpolation_method default 'balanced_tangential', - end_distance real default 25, - end_angle real default 5.0, correlation_distance real default 200, correlation_angle real default 5.0, - parent_correlation_angle real default 1.0) + parent_correlation_angle real default 1.0, + end_node_relative_distance real default .3, + end_node_relative_thickness real default .3, + version varchar) ; -insert into _albion.metadata(srid) select $SRID +insert into _albion.metadata(srid, version) select $SRID, '2.0' ; -create table _albion.collar( - id varchar primary key default _albion.unique_id()::varchar, - x double precision, - y double precision, - z real, - date_ varchar, - geom geometry('POINTZ', $SRID), - comments varchar) -; - -create index collar_geom_idx on _albion.collar using gist(geom) +create table _albion.layer( + name varchar primary key, + fields_definition text not null) ; create table _albion.hole( - id varchar primary key, - collar_id varchar unique not null references _albion.collar(id) on delete cascade on update cascade, - depth_ real, + id varchar primary key default _albion.unique_id()::varchar, + date_ varchar, + depth_ real not null, + check(depth_ > 0), + x double precision not null, + y double precision not null, + z double precision not null, + comments varchar, geom geometry('LINESTRINGZ', $SRID)) ; -create index hole_geom_idx on _albion.hole using gist(geom) +alter table _albion.hole add constraint hole_geom_length_chk check (geom is null or abs(st_3dlength(geom) - depth_) <= 1e-3) ; -create index hole_collar_id_idx on _albion.hole(collar_id) -; - - -alter table _albion.hole alter column id set default _albion.unique_id()::varchar +create index hole_geom_idx on _albion.hole using gist(geom) ; -------------------------------------------------------------------------------- --- MEASURES -------------------------------------------------------------------------------- - create table _albion.deviation( hole_id varchar not null references _albion.hole(id) on delete cascade on update cascade, from_ real, @@ -83,147 +73,13 @@ create table _albion.deviation( azimuth real) ; -create index deviation_hole_id_idx on _albion.deviation(hole_id) -; - -create table _albion.radiometry( - id varchar primary key, - hole_id varchar not null references _albion.hole(id) on delete cascade on update cascade, - from_ real, - to_ real, - gamma real, - geom geometry('LINESTRINGZ', $SRID)) -; - -create index radiometry_geom_idx on _albion.radiometry using gist(geom) -; - -create index radiometry_hole_id_idx on _albion.radiometry(hole_id) -; - -alter table _albion.radiometry alter column id set default _albion.unique_id()::varchar -; - -create table _albion.resistivity( - id varchar primary key, - hole_id varchar not null references _albion.hole(id) on delete cascade on update cascade, - from_ real, - to_ real, - rho real, - geom geometry('LINESTRINGZ', $SRID)) -; - -create index resistivity_geom_idx on _albion.resistivity using gist(geom) -; - -create index resistivity_hole_id_idx on _albion.resistivity(hole_id) -; - -alter table _albion.resistivity alter column id set default _albion.unique_id()::varchar -; - - -create table _albion.formation( - id varchar primary key, - hole_id varchar not null references _albion.hole(id) on delete cascade on update cascade, - from_ real, - to_ real, - code integer, - comments varchar, - geom geometry('LINESTRINGZ', $SRID)) -; - -create index formation_geom_idx on _albion.formation using gist(geom) -; - -create index formation_hole_id_idx on _albion.formation(hole_id) -; - -alter table _albion.formation alter column id set default _albion.unique_id()::varchar -; - -create table _albion.lithology( - id varchar primary key, - hole_id varchar not null references _albion.hole(id) on delete cascade on update cascade, - from_ real, - to_ real, - code integer, - comments varchar, - geom geometry('LINESTRINGZ', $SRID)) -; - -create index lithology_geom_idx on _albion.lithology using gist(geom) -; - -create index lithology_hole_id_idx on _albion.lithology(hole_id) -; - -alter table _albion.lithology alter column id set default _albion.unique_id()::varchar -; - -create table _albion.facies( - id varchar primary key, - hole_id varchar not null references _albion.hole(id) on delete cascade on update cascade, - from_ real, - to_ real, - code integer, - comments varchar, - geom geometry('LINESTRINGZ', $SRID)) -; - -create index facies_geom_idx on _albion.facies using gist(geom) -; - -create index facies_hole_id_idx on _albion.facies(hole_id) -; - -alter table _albion.facies alter column id set default _albion.unique_id()::varchar -; - -create table _albion.chemical( - hole_id varchar not null references _albion.hole(id) on delete cascade on update cascade, - from_ real, - to_ real, - num_sample varchar, - element varchar, - thickness real, - gt real, -- thickness * grade - grade real, - equi real, - comments varchar) -; - -create index chemical_hole_id_idx on _albion.chemical(hole_id) -; - -create table _albion.mineralization( - id varchar primary key, - hole_id varchar not null references _albion.hole(id) on delete cascade on update cascade, - level_ real, - from_ real, - to_ real, - oc real, - accu real, - grade real, - comments varchar, - geom geometry('LINESTRINGZ', $SRID)) -; - -create index mineralization_geom_idx on _albion.mineralization using gist(geom) -; - -create index mineralization_hole_id_idx on _albion.mineralization(hole_id) -; - -alter table _albion.mineralization alter column id set default _albion.unique_id()::varchar -; ------------------------------------------------------------------------------- -- GRAPH ------------------------------------------------------------------------------- create table _albion.node( - id varchar primary key, + id varchar primary key default _albion.unique_id()::varchar, graph_id varchar not null references _albion.graph(id) on delete cascade on update cascade, unique(id, graph_id), hole_id varchar references _albion.hole(id) on delete cascade, @@ -243,11 +99,8 @@ create index node_graph_id_idx on _albion.node(graph_id) create index node_hole_id_idx on _albion.node(hole_id) ; -alter table _albion.node alter column id set default _albion.unique_id()::varchar -; - create table _albion.edge( - id varchar primary key, + id varchar primary key default _albion.unique_id()::varchar, start_ varchar not null , foreign key (graph_id, start_) references _albion.node(graph_id, id) on delete cascade on update cascade, end_ varchar not null, @@ -271,14 +124,11 @@ create index edge_start__idx on _albion.edge(start_) create index edge_end__idx on _albion.edge(end_) ; -alter table _albion.edge alter column id set default _albion.unique_id()::varchar -; - create table _albion.cell( - id varchar primary key, - a varchar not null references _albion.collar(id) on delete cascade on update cascade, - b varchar not null references _albion.collar(id) on delete cascade on update cascade, - c varchar not null references _albion.collar(id) on delete cascade on update cascade, + id varchar primary key default _albion.unique_id()::varchar, + a varchar not null references _albion.hole(id) on delete cascade on update cascade, + b varchar not null references _albion.hole(id) on delete cascade on update cascade, + c varchar not null references _albion.hole(id) on delete cascade on update cascade, geom geometry('POLYGON', $SRID) not null check(st_isvalid(geom) and st_numpoints(geom)=4) ) ; @@ -295,32 +145,29 @@ create index volume_cell_b_idx on _albion.cell(b) create index volume_cell_c_idx on _albion.cell(c) ; -alter table _albion.cell alter column id set default _albion.unique_id()::varchar -; - create table _albion.group( id integer primary key ) ; create table _albion.section( - id varchar primary key, + id varchar primary key default _albion.unique_id()::varchar, anchor geometry('LINESTRING', $SRID) not null check(st_numpoints(anchor)=2), - geom geometry('LINESTRING', $SRID) not null, - scale real not null default 1, - group_id integer references _albion.group(id) on delete set null on update cascade + geom geometry('MULTILINESTRING', $SRID), + scale real not null default 1 ) ; -alter table _albion.section alter column id set default _albion.unique_id()::varchar -; - create table _albion.volume( - id varchar primary key, + id varchar primary key default _albion.unique_id()::varchar, graph_id varchar not null references _albion.graph(id) on delete cascade on update cascade, cell_id varchar not null references _albion.cell(id) on delete cascade on update cascade, - triangulation geometry('MULTIPOLYGONZ', $SRID) not null -); + triangulation geometry('MULTIPOLYGONZ', $SRID) not null, + face1 geometry('MULTIPOLYGONZ', $SRID), + face2 geometry('MULTIPOLYGONZ', $SRID), + face3 geometry('MULTIPOLYGONZ', $SRID) +) +; create index volume_graph_id_idx on _albion.volume(graph_id) ; @@ -328,9 +175,6 @@ create index volume_graph_id_idx on _albion.volume(graph_id) create index volume_cell_id_idx on _albion.volume(cell_id) ; -alter table _albion.volume alter column id set default _albion.unique_id()::varchar -; - create table _albion.group_cell( group_id integer not null references _albion.group(id) on delete cascade on update cascade, cell_id varchar not null references _albion.cell(id) on delete cascade on update cascade, @@ -347,10 +191,10 @@ create index group_cell_groupe_id_idx on _albion.group_cell(group_id) create table _albion.end_node( - id varchar primary key, + id varchar primary key default _albion.unique_id()::varchar, geom geometry('LINESTRINGZ', $SRID) not null check (st_numpoints(geom)=2), node_id varchar not null references _albion.node(id) on delete cascade on update cascade, - collar_id varchar not null references _albion.collar(id) on delete cascade on update cascade, + hole_id varchar not null references _albion.hole(id) on delete cascade on update cascade, graph_id varchar references _albion.graph(id) on delete cascade ) ; @@ -358,35 +202,22 @@ create table _albion.end_node( create index end_node_geom_idx on _albion.end_node using gist(geom) ; -alter table _albion.end_node alter column id set default _albion.unique_id()::varchar -; - ---create table _albion.end_edge( --- id varchar primary key, --- start_ varchar not null , --- foreign key (graph_id, start_) references _albion.end_node(graph_id, id) on delete cascade on update cascade, --- end_ varchar not null, --- foreign key (graph_id, end_) references _albion.end_node(graph_id, id) on delete cascade on update cascade, --- unique (start_, end_), --- check (start_ < end_), --- graph_id varchar references _albion.graph(id) on delete cascade, --- geom geometry('LINESTRINGZ', $SRID) not null check (st_isvalid(geom)) ---) ---; --- ---create index end_edge_geom_idx on _albion.end_edge using gist(geom) ---; --- ---create index end_edge_graph_id_idx on _albion.end_edge(graph_id) ---; --- ---create index end_edge_start__idx on _albion.end_edge(start_) ---; --- ---create index end_edge_end__idx on _albion.end_edge(end_) ---; --- ---alter table _albion.end_edge alter column id set default _albion.unique_id()::varchar ---; +create table _albion.named_section( + id varchar primary key default _albion.unique_id()::varchar, + geom geometry('LINESTRING', $SRID) not null, + cut geometry('MULTILINESTRING', $SRID) not null, + section varchar not null references _albion.section(id) on delete cascade on update cascade +) +; + +create table _albion.vertical_face( + id varchar primary key default _albion.unique_id()::varchar, + graph_id varchar not null references _albion.graph(id) on delete cascade on update cascade, + left_hole_id varchar not null references _albion.hole(id) on delete cascade on update cascade, + right_hole_id varchar not null references _albion.hole(id) on delete cascade on update cascade, + triangulation geometry('MULTIPOLYGONZ', $SRID) not null +); + + diff --git a/_albion_table.sql b/_albion_table.sql new file mode 100644 index 0000000..53014c4 --- /dev/null +++ b/_albion_table.sql @@ -0,0 +1,10 @@ +create table _albion.$NAME( + id varchar primary key default _albion.unique_id()::varchar, + hole_id varchar not null references _albion.hole(id) on delete cascade on update cascade, + from_ real check (from_>=0), + to_ real check (to_>=0), + ${FIELDS_DEFINITION}) +; + +insert into _albion.layer(name, fields_definition) values ('$NAME', '$FIELDS_DEFINITION') +; diff --git a/_albion_v1_to_v2.sql b/_albion_v1_to_v2.sql new file mode 100644 index 0000000..2d4456d --- /dev/null +++ b/_albion_v1_to_v2.sql @@ -0,0 +1,113 @@ +-- changed metadata +alter table _albion.metadata drop column end_angle +; +alter table _albion.metadata drop column end_distance +; +alter table _albion.metadata add column end_node_relative_distance real default .3 +; +alter table _albion.metadata add column version varchar default '2.0' +; +alter table _albion.metadata add column end_node_relative_thickness real default .3 +; + +-- add layer table +create table _albion.layer( + name varchar primary key, + fields_definition text not null) +; + +insert into _albion.layer(name, fields_definition) +select t.name, t.fields_definition +from (VALUES + ('radiometry', 'gamma real'), + ('resistivity', 'rho real'), + ('formation', 'code integer, comments varchar'), + ('lithology', 'code integer, comments varchar'), + ('facies', 'code integer, comments varchar'), + ('chemical', 'num_sample varchar, element varchar, thickness real, gt real, grade real, equi real, comments varchar'), + ('mineralization', 'level_ real, oc real, accu real, grade real, comments varchar') + ) as t(name, fields_definition) +join information_schema.tables on table_schema = '_albion' and table_name = t.name +; + +alter table if exists _albion.chemical add column id varchar primary key default _albion.unique_id()::varchar +; + +-- merge collar and hole tables +alter table _albion.hole alter column id set default _albion.unique_id()::varchar +; +alter table _albion.hole add column date_ varchar +; +alter table _albion.hole add constraint depth_check check(depth_ > 0) +; +alter table _albion.hole add column x double precision +; +alter table _albion.hole add column y double precision +; +alter table _albion.hole add column z double precision +; +alter table _albion.hole add column comments varchar +; +update _albion.hole as h set x=c.x, y=c.y, z=c.z, date_=c.date_, comments=c.comments +from _albion.collar as c where h.collar_id=c.id +; +alter table _albion.hole alter column x set not null +; +alter table _albion.hole alter column y set not null +; +alter table _albion.hole alter column z set not null +; +alter table _albion.hole drop column collar_id +; +alter table _albion.hole add constraint hole_geom_length_chk check (geom is null or abs(st_3dlength(geom) - depth_) <= 1e-3) +; + +-- cell now references holes rather than collar +alter table _albion.cell drop constraint cell_a_fkey +; +alter table _albion.cell drop constraint cell_b_fkey +; +alter table _albion.cell drop constraint cell_c_fkey +; +alter table _albion.cell add constraint cell_a_fkey foreign key(a) REFERENCES _albion.hole(id); +; +alter table _albion.cell add constraint cell_b_fkey foreign key(b) REFERENCES _albion.hole(id); +; +alter table _albion.cell add constraint cell_c_fkey foreign key(c) REFERENCES _albion.hole(id); +; + +-- change section +alter table _albion.section alter column geom type geometry('MULTILINESTRING', $SRID) using st_multi(geom) +; +alter table _albion.section drop column group_id +; + +-- end_node now reference hole rather than collar +alter table _albion.end_node drop constraint end_node_collar_id_fkey; +; +alter table _albion.end_node rename column collar_id to hole_id +; +alter table _albion.end_node add constraint end_node_hole_id_fkey foreign key(hole_id) REFERENCES _albion.hole(id); +; + +drop table _albion.collar +; + +alter table _albion.volume add column face1 geometry('MULTIPOLYGONZ', $SRID) +; + +alter table _albion.volume add column face2 geometry('MULTIPOLYGONZ', $SRID) +; + +alter table _albion.volume add column face3 geometry('MULTIPOLYGONZ', $SRID) +; + +-- adds named_section +create table _albion.named_section( + id varchar primary key default _albion.unique_id()::varchar, + geom geometry('LINESTRING', $SRID) not null, + cut geometry('MULTILINESTRING', $SRID) not null, + section varchar not null references _albion.section(id) on delete cascade on update cascade +) +; + diff --git a/albion.sql b/albion.sql index 7d915f3..d04fd1a 100644 --- a/albion.sql +++ b/albion.sql @@ -8,61 +8,27 @@ create schema albion ------------------------------------------------------------------------------- -- UTILITY FUNCTIONS ------------------------------------------------------------------------------- ---create or replace function public.st_3dlineinterpolatepoint(line_ geometry, sigma_ float) ---returns geometry ---language plpython3u immutable ---as ---$$ --- --- from shapely import wkb --- from shapely import geos --- from shapely.geometry import Point --- import numpy as np --- from numpy import array --- from numpy.linalg import norm --- geos.WKBWriter.defaults['include_srid'] = True --- --- if line_ is None: --- return None --- --- assert(sigma_ >=0 and sigma_ <=1) --- --- line = wkb.loads(bytes.fromhex(line_)) --- --- if sigma_ > 0 and sigma_ < 1: --- l = array(line.coords) --- seg = l[:-1] - l[1:] --- seg_length = np.sum(seg**2,axis=-1)**.5 --- line_length = np.sum(seg_length) --- target_length = line_length*sigma_ --- cum_length = np.cumsum(seg_length) --- idx = np.searchsorted(np.cumsum(seg_length), target_length) --- overshoot = cum_length[idx] - target_length --- a = overshoot/seg_length[idx] --- result = Point(l[idx]*a + l[idx+1]*(1.-a)) --- --- elif sigma_ == 1: --- result = Point(line.coords[-1]) --- --- elif sigma_ == 0: --- result = Point(line.coords[0]) --- --- geos.lgeos.GEOSSetSRID(result._geom, geos.lgeos.GEOSGetSRID(line._geom)) --- return result.wkb_hex --- ---$$ ---; ---create or replace function albion.srid() ---returns integer ---language plpgsql stable ---as ---$$ --- begin --- return select (srid from _albion.metadata); --- end; ---$$ ---; +create or replace function albion.triangle_aspect_ratio(geom geometry) +returns float +language plpgsql +as +$$ + declare + a float; + b float; + c float; + s float; + begin + a := st_distance(st_pointn(st_exteriorring(geom), 1), st_pointn(st_exteriorring(geom), 2)); + b := st_distance(st_pointn(st_exteriorring(geom), 2), st_pointn(st_exteriorring(geom), 3)); + c := st_distance(st_pointn(st_exteriorring(geom), 3), st_pointn(st_exteriorring(geom), 1)); + s := (a+b+c)/2; + return a*b*c/(8*(s-a)*(s-b)*(s-c)); + + end; +$$ +; create or replace function albion.hole_geom(hole_id_ varchar) returns geometry @@ -72,13 +38,15 @@ $$ declare depth_max_ real; hole_geom_ geometry; - collar_id_ varchar; + x_ double precision; + y_ double precision; + z_ double precision; collar_geom_ geometry; + path_ varchar; begin - select collar_id, depth_ from albion.hole where id=hole_id_ into collar_id_, depth_max_; - - select geom from albion.collar where id=collar_id_ into collar_geom_; + select x, y, z, depth_ from _albion.hole where id=hole_id_ into x_, y_, z_, depth_max_; + collar_geom_ := st_setsrid(st_makepoint(x_, y_, z_), $SRID); with dz as ( select from_ as md2, coalesce(lag(from_) over w, 0) as md1, @@ -91,9 +59,9 @@ $$ ), pt as ( select md2, wd2, haz2, - st_x(collar_geom_) + sum(0.5 * (md2 - md1) * (sin(wd1) * sin(haz1) + sin(wd2) * sin(haz2))) over w as x, - st_y(collar_geom_) + sum(0.5 * (md2 - md1) * (sin(wd1) * cos(haz1) + sin(wd2) * cos(haz2))) over w as y, - st_z(collar_geom_) - sum(0.5 * (md2 - md1) * (cos(wd2) + cos(wd1))) over w as z + x_ + sum(0.5 * (md2 - md1) * (sin(wd1) * sin(haz1) + sin(wd2) * sin(haz2))) over w as x, + y_ + sum(0.5 * (md2 - md1) * (sin(wd1) * cos(haz1) + sin(wd2) * cos(haz2))) over w as y, + z_ - sum(0.5 * (md2 - md1) * (cos(wd2) + cos(wd1))) over w as z from dz window w AS (order by md1) ), @@ -106,6 +74,7 @@ $$ into hole_geom_; if hole_geom_ is not null and st_3dlength(hole_geom_) < depth_max_ and st_3dlength(hole_geom_) > 0 then + path_ := 'too short'; -- holes is not long enough with last_segment as ( select st_pointn(hole_geom_, st_numpoints(hole_geom_)-1) as start_, st_endpoint(hole_geom_) as end_ @@ -128,9 +97,13 @@ $$ -- hole have no deviation elsif hole_geom_ is null or st_3dlength(hole_geom_) = 0 then + path_ := 'no length'; select st_makeline( collar_geom_, st_translate(collar_geom_, 0, 0, -depth_max_)) into hole_geom_; end if; + if abs(st_3dlength(hole_geom_) - depth_max_) > 1e-3 then + raise 'hole %s %s %s %', hole_id_, depth_max_, st_3dlength(hole_geom_), path_; + end if; return hole_geom_; end; $$ @@ -144,45 +117,64 @@ $$ begin return ( select st_makeline( - st_3dlineinterpolatepoint(geom, from_/depth_), - st_3dlineinterpolatepoint(geom, to_/depth_)) - from albion.hole where id=hole_id_ + st_3dlineinterpolatepoint(geom, least(from_/l, 1)), + st_3dlineinterpolatepoint(geom, least(to_/l, 1))) + from (select geom, st_3dlength(geom) as l from albion.hole where id=hole_id_) as t ); end; $$ ; -create view albion.collar as select id, geom, date_, comments from _albion.collar +create or replace view albion.collar as select id, st_startpoint(geom)::geometry('POINTZ', $SRID) as geom, date_, comments, depth_ from _albion.hole ; -create view albion.metadata as select id, srid, close_collar_distance, snap_distance, precision, interpolation, end_distance, end_angle, correlation_distance, correlation_angle, parent_correlation_angle from _albion.metadata +alter view albion.collar alter id set default _albion.unique_id()::varchar ; -create view albion.hole as select id, collar_id, depth_, geom::geometry('LINESTRINGZ', $SRID) from _albion.hole -; - -create view albion.deviation as select hole_id, from_, dip, azimuth from _albion.deviation -; - -create view albion.formation as select id, hole_id, from_, to_, code, comments, geom::geometry('LINESTRINGZ', $SRID) from _albion.formation -; +create or replace function albion.collar_instead_fct() +returns trigger +language plpgsql +as +$$ + begin + if tg_op in ('INSERT', 'UPDATE') then + new.date_ := coalesce(new.date_, now()::date::varchar); + end if; -create view albion.resistivity as select id, hole_id, from_, to_, rho, geom::geometry('LINESTRINGZ', $SRID) from _albion.resistivity + if tg_op = 'INSERT' then + insert into _albion.hole(id, date_, depth_, x, y, z, comments) + values(new.id, new.date_, new.depth_, st_x(new.geom), st_y(new.geom), st_z(new.geom), new.comments) + returning id into new.id; + update _albion.hole set geom = albion.hole_geom(new.id) where id=new.id; + return new; + elsif tg_op = 'UPDATE' then + update _albion.hole set id=new.id, date_=new.date_, depth_=new.depth_, x=st_x(new.geom), y=st_y(new.geom), z=st_z(new.geom), comments=new.comments + where id=old.id; + update _albion.hole set geom = albion.hole_geom(new.id) where id=new.id; + return new; + elsif tg_op = 'DELETE' then + delete from _albion.collar where id=old.id; + return old; + end if; + end; +$$ ; -create view albion.radiometry as select id, hole_id, from_, to_, gamma, geom::geometry('LINESTRINGZ', $SRID) from _albion.radiometry +create trigger collar_instead_trig + instead of insert or update or delete on albion.collar + for each row execute procedure albion.collar_instead_fct() ; -create view albion.lithology as select id, hole_id, from_, to_, code, comments, geom::geometry('LINESTRINGZ', $SRID) from _albion.lithology +create view albion.metadata as select id, srid, close_collar_distance, snap_distance, precision, interpolation, end_node_relative_distance, end_node_relative_thickness, correlation_distance, correlation_angle, parent_correlation_angle from _albion.metadata ; -create view albion.facies as select id, hole_id, from_, to_, code, comments, geom::geometry('LINESTRINGZ', $SRID) from _albion.facies +create view albion.layer as select name, fields_definition from _albion.layer ; -create view albion.chemical as select hole_id, from_, to_, num_sample, element, thickness, gt, grade, equi, comments from _albion.chemical +create view albion.hole as select id, depth_, geom::geometry('LINESTRINGZ', $SRID) from _albion.hole ; -create view albion.mineralization as select id, hole_id, level_, from_, to_, oc, accu, grade, geom::geometry('LINESTRINGZ', $SRID) from _albion.mineralization +create view albion.deviation as select hole_id, from_, dip, azimuth from _albion.deviation ; create or replace view albion.graph as @@ -213,6 +205,8 @@ $$ end if; end if; + new.geom := coalesce(new.geom, albion.hole_piece(new.from_, new.to_, new.hole_id)); + if tg_op = 'INSERT' then insert into _albion.node(id, graph_id, hole_id, from_, to_, geom, parent) values(new.id, new.graph_id, new.hole_id, new.from_, new.to_, new.geom, new.parent) @@ -239,11 +233,52 @@ create trigger node_instead_trig create or replace view albion.close_collar as -select a.id, a.geom from _albion.collar as a, _albion.collar as b, _albion.metadata as m +select distinct on (a.id) a.id, a.geom from albion.collar as a, albion.collar as b, _albion.metadata as m where a.id != b.id and st_dwithin(a.geom, b.geom, m.close_collar_distance) ; -create view albion.cell as select id, a, b, c, geom::geometry('POLYGON', $SRID) from _albion.cell +create view albion.cell as select id, a, b, c, geom::geometry('POLYGON', $SRID), albion.triangle_aspect_ratio(geom) as aspect_ratio from _albion.cell +; + +create or replace function albion.cell_after_fct() +returns trigger +language plpgsql +as +$$ + begin + refresh materialized view albion.all_edge; + return null; + end; +$$ +; + +-- this trigger should work on the view instead of the table, but for unknown reason it doesn't, so we put it on the table +drop trigger if exists cell_after_trig ON _albion.cell +; + +create trigger cell_after_trig + after delete on _albion.cell + for each statement execute procedure albion.cell_after_fct() +; + +create or replace function albion.tesselate(polygon_ geometry, lines_ geometry, points_ geometry) +returns geometry +language plpython3u volatile +as +$$ + from shapely import wkb + from shapely import geos + geos.WKBWriter.defaults['include_srid'] = True + from fourmy import tessellate + + polygon = wkb.loads(bytes.fromhex(polygon_)) + lines = wkb.loads(bytes.fromhex(lines_)) if lines_ else None + points = wkb.loads(bytes.fromhex(points_)) if points_ else None + result = tessellate(polygon, lines, points) + + geos.lgeos.GEOSSetSRID(result._geom, geos.lgeos.GEOSGetSRID(polygon._geom)) + return result.wkb_hex +$$ ; create or replace function albion.triangulate() @@ -255,18 +290,24 @@ $$ delete from _albion.cell; insert into _albion.cell(a, b, c, geom) with cell as ( - select ST_DelaunayTriangles(ST_Collect(ST_Force2D(geom))) as geom from _albion.collar + select albion.tesselate( + st_convexhull((select st_collect(st_force2d(geom)) from albion.collar)), + st_multi((select st_collectionhomogenize(st_collect(cut)) from albion.named_section)), + st_multi((select st_collect(st_force2d(geom)) from albion.collar)) + ) as geom ), splt as ( select (ST_Dump(geom)).geom from cell ) select - (select c.id from _albion.collar as c where st_intersects(c.geom, st_pointn(st_exteriorring(s.geom), 1))), - (select c.id from _albion.collar as c where st_intersects(c.geom, st_pointn(st_exteriorring(s.geom), 2))), - (select c.id from _albion.collar as c where st_intersects(c.geom, st_pointn(st_exteriorring(s.geom), 3))), + (select c.id from albion.collar as c where st_intersects(c.geom, st_pointn(st_exteriorring(s.geom), 1))), + (select c.id from albion.collar as c where st_intersects(c.geom, st_pointn(st_exteriorring(s.geom), 2))), + (select c.id from albion.collar as c where st_intersects(c.geom, st_pointn(st_exteriorring(s.geom), 3))), s.geom from splt as s; + refresh materialized view albion.all_edge; + return (select count(1) from _albion.cell); end; $$ @@ -292,39 +333,6 @@ $$ $$ ; --- contour of the cells that face the line -create or replace function albion.first_section(anchor geometry) -returns geometry -language plpgsql stable -as -$$ - begin - return ( - with hull as ( - select st_exteriorring(st_unaryunion(st_collect(geom))) as geom from _albion.cell - ), - seg as ( - select ST_PointN(geom, generate_series(1, ST_NPoints(geom)-1)) as sp, ST_PointN(geom, generate_series(2, ST_NPoints(geom) )) as ep - from hull - ), - facing as ( - select st_force2d(st_makeline(sp, ep)) as geom - from seg - where -albion.cos_angle(anchor, sp, ep) > cos(60*pi()/180) - ), - merged as ( - select st_linemerge(st_collect(geom)) as geom from facing - ), - sorted as ( - select rank() over(order by st_length(geom) desc) as rk, geom - from (select (st_dump(geom)).geom from merged) as t - ) - select geom from sorted where rk=1 - ); - end; -$$ -; - create or replace function albion.is_touchingrightside(line geometry, poly geometry) returns boolean language plpgsql immutable @@ -390,7 +398,7 @@ select albion.is_touchingrightside('LINESTRING(327627.06 2079630.27,327229.65 20 ; --- triangle is visible if points are on the line, or by removing the trinagle edge that touch +-- polygon is visible if points are on the line, or by removing the edge that touches -- the line, the line of sight from anchor to point doesn't cross the line create or replace function albion.is_visible(anchor geometry, section geometry, poly geometry) returns boolean @@ -401,24 +409,30 @@ $$ nb_visible integer; ring geometry; occluder geometry; + point_on_poly geometry; line_od_sight geometry; begin nb_visible := 0; ring := st_exteriorring(poly); - for i in 1..st_numpoints(ring) loop - if st_intersects(section, st_pointn(ring, i)) then - nb_visible := nb_visible + 1; - else - occluder := coalesce(occluder, st_difference(section, ring)); - line_od_sight := st_makeline(st_closestpoint(anchor, st_pointn(ring, i)), st_pointn(ring, i)); - --raise notice 'occluder %', st_astext(occluder); - --raise notice 'los %', st_astext(line_od_sight); - if not st_intersects(line_od_sight, occluder) then + occluder := st_difference(section, ring); + if occluder is not null then + for i in 1..st_numpoints(ring) loop + if st_intersects(section, st_pointn(ring, i)) then nb_visible := nb_visible + 1; + else + line_od_sight := st_makeline(st_closestpoint(anchor, st_pointn(ring, i)), st_pointn(ring, i)); + --raise notice 'occluder %', st_astext(occluder); + --raise notice 'los %', st_astext(line_od_sight); + if not st_intersects(line_od_sight, occluder) then + nb_visible := nb_visible + 1; + end if; end if; - end if; - end loop; - return nb_visible = st_numpoints(ring); + end loop; + end if; + -- we also check that the line between a point on surface and the anchor crosses the section (we are looking "away" from anchor) + point_on_poly := st_pointonsurface(poly); + line_od_sight := st_makeline(st_closestpoint(anchor, point_on_poly), point_on_poly); + return nb_visible = st_numpoints(ring) and st_intersects(line_od_sight, section); end; $$ ; @@ -457,7 +471,7 @@ $$ $$ ; -create view albion.section as select id, scale, group_id, anchor::geometry('LINESTRING', $SRID), geom::geometry('LINESTRING', $SRID) +create view albion.section as select id, scale, anchor::geometry('LINESTRING', $SRID), geom::geometry('MULTILINESTRING', $SRID) from _albion.section ; @@ -474,12 +488,12 @@ as $$ begin if tg_op = 'INSERT' then - insert into _albion.section(id, anchor, geom, scale, group_id) - values(new.id, new.anchor, coalesce(new.geom, albion.first_section(new.anchor)), new.scale, new.group_id) + insert into _albion.section(id, anchor, geom, scale) + values(new.id, new.anchor, new.geom, new.scale) returning id, geom into new.id, new.geom; return new; elsif tg_op = 'UPDATE' then - update _albion.section set id=new.id, anchor=new.anchor, geom=new.geom, scale=new.scale, group_id=new.group_id + update _albion.section set id=new.id, anchor=new.anchor, geom=new.geom, scale=new.scale where id=old.id; return new; elsif tg_op = 'DELETE' then @@ -496,24 +510,10 @@ create trigger section_instead_trig ; -create or replace function albion.next_group() -returns integer -language plpgsql stable -as -$$ - begin - return (select coalesce(max(id), 0) + 1 from _albion.group); - end; -$$ -; - create view albion.group as select id from _albion.group ; -alter view albion.group alter column id set default albion.next_group() -; - create view albion.group_cell as select gc.section_id || ' ' || gc.cell_id as id, gc.cell_id, c.geom, gc.group_id, gc.section_id from _albion.cell as c @@ -546,40 +546,6 @@ create trigger group_cell_instead_trig for each row execute procedure albion.group_cell_instead_fct() ; -create view albion.current_section as -with hull as ( - select st_unaryunion(st_collect(c.geom)) as geom, gc.section_id - from _albion.cell as c - join _albion.group_cell as gc on gc.cell_id=c.id - group by gc.section_id -), -hull_contour as ( - select st_exteriorring(geom) as geom, section_id from hull -), -seg as ( - select ST_PointN(geom, generate_series(1, ST_NPoints(geom)-1)) as sp, ST_PointN(geom, generate_series(2, ST_NPoints(geom) )) as ep, section_id - from hull_contour -), -facing as ( - select st_force2d(st_makeline(seg.sp, seg.ep)) as geom, seg.section_id - from seg join _albion.section as s on s.id = seg.section_id - where albion.cos_angle(s.anchor, seg.sp, seg.ep) > cos(89*pi()/180) -), -merged as ( - select st_linemerge(st_collect(facing.geom)) as geom, section_id - from facing join _albion.section as s on s.id = facing.section_id - group by section_id, s.geom -), -sorted as ( - select rank() over(partition by section_id order by st_length(geom) desc) as rk, geom, section_id - from (select (st_dump(geom)).geom, section_id from merged) as t -) -select section_id as id, st_reverse(geom)::geometry('LINESTRING', $SRID) as geom from sorted where rk=1 -union all -select s.id, (albion.first_section(s.anchor))::geometry('LINESTRING', $SRID) as geom from albion.section as s -where not exists (select 1 from sorted as st where st.section_id=s.id) -; - create or replace function albion.to_section(geom geometry, anchor geometry, z_scale real) returns geometry language plpython3u immutable @@ -667,112 +633,22 @@ $$ $$ ; -create materialized view albion.radiometry_section as -select r.id as radiometry_id, s.id as section_id, - (albion.to_section(r.geom, s.anchor, s.scale))::geometry('LINESTRING', $SRID) as geom -from _albion.radiometry as r -join _albion.hole as h on h.id=r.hole_id, _albion.section as s -; - -create index radiometry_section_geom_idx on albion.radiometry_section using gist(geom) -; - -create index radiometry_section_radiometry_id_idx on albion.radiometry_section(radiometry_id) -; - -create materialized view albion.resistivity_section as -select r.id as resistivity_id, s.id as section_id, - (albion.to_section(r.geom, s.anchor, s.scale))::geometry('LINESTRING', $SRID) as geom -from _albion.resistivity as r -join _albion.hole as h on h.id=r.hole_id, _albion.section as s -; - -create index resistivity_section_geom_idx on albion.resistivity_section using gist(geom) -; - -create index resistivity_section_resistivity_id_idx on albion.resistivity_section(resistivity_id) -; - -create view albion.current_node_section as -select row_number() over() as id, n.id as node_id, h.collar_id, n.from_, n.to_, n.graph_id, s.id as section_id, - (albion.to_section(n.geom, s.anchor, s.scale))::geometry('LINESTRING', $SRID) as geom -from _albion.node as n -join _albion.hole as h on h.id=n.hole_id -join _albion.collar as c on c.id=h.collar_id -join _albion.section as s on st_intersects(s.geom, c.geom) -; - create view albion.hole_section as -select row_number() over() as id, h.id as hole_id, h.collar_id, h.depth_, s.id as section_id, +select row_number() over() as id, h.id as hole_id, h.depth_, s.id as section_id, (albion.to_section(h.geom, s.anchor, s.scale))::geometry('LINESTRING', $SRID) as geom -from _albion.hole as h -join _albion.collar as c on c.id=h.collar_id, _albion.section as s +from _albion.section as s +join _albion.hole as h on s.geom && h.geom and st_intersects(st_startpoint(h.geom), s.geom) ; -create view albion.current_hole_section as -select row_number() over() as id, h.id as hole_id, h.collar_id, h.depth_, s.id as section_id, - (albion.to_section(h.geom, s.anchor, s.scale))::geometry('LINESTRING', $SRID) as geom -from _albion.hole as h -join _albion.collar as c on c.id=h.collar_id -join _albion.section as s on st_intersects(s.geom, c.geom) -; -create view albion.current_mineralization_section as -select row_number() over() as id, m.hole_id as hole_id, h.collar_id, m.level_, m.oc, m.accu, m.grade, s.id as section_id, - (albion.to_section(m.geom, s.anchor, s.scale))::geometry('LINESTRING', $SRID) as geom -from _albion.mineralization as m -join _albion.hole as h on h.id=m.hole_id -join _albion.collar as c on c.id=h.collar_id -join _albion.section as s on st_intersects(s.geom, c.geom) +create view albion.node_section as +select row_number() over() as id, n.id as node_id, h.id as hole_id, n.from_, n.to_, n.graph_id, s.id as section_id, + (albion.to_section(n.geom, s.anchor, s.scale))::geometry('LINESTRING', $SRID) as geom, n.parent +from _albion.section as s +join _albion.hole as h on s.geom && h.geom and st_intersects(st_startpoint(h.geom), s.geom) +join _albion.node as n on n.hole_id = h.id ; -create view albion.current_radiometry_section as -select row_number() over() as id, r.hole_id as hole_id, h.collar_id, r.gamma, s.id as section_id, - rs.geom::geometry('LINESTRING', $SRID) as geom -from _albion.radiometry as r -join _albion.hole as h on h.id=r.hole_id -join _albion.collar as c on c.id=h.collar_id -join _albion.section as s on st_intersects(s.geom, c.geom) -join albion.radiometry_section as rs on rs.radiometry_id=r.id and rs.section_id=s.id -; - -create view albion.current_resistivity_section as -select row_number() over() as id, r.hole_id as hole_id, h.collar_id, r.rho, s.id as section_id, - rs.geom::geometry('LINESTRING', $SRID) as geom -from _albion.resistivity as r -join _albion.hole as h on h.id=r.hole_id -join _albion.collar as c on c.id=h.collar_id -join _albion.section as s on st_intersects(s.geom, c.geom) -join albion.resistivity_section as rs on rs.resistivity_id=r.id and rs.section_id=s.id -; - - -create view albion.current_formation_section as -select row_number() over() as id, r.hole_id as hole_id, h.collar_id, r.code, r.comments, s.id as section_id, - (albion.to_section(r.geom, s.anchor, s.scale))::geometry('LINESTRING', $SRID) as geom -from _albion.formation as r -join _albion.hole as h on h.id=r.hole_id -join _albion.collar as c on c.id=h.collar_id -join _albion.section as s on st_intersects(s.geom, c.geom) -; - -create view albion.current_lithology_section as -select row_number() over() as id, r.hole_id as hole_id, h.collar_id, r.code, r.comments, s.id as section_id, - (albion.to_section(r.geom, s.anchor, s.scale))::geometry('LINESTRING', $SRID) as geom -from _albion.lithology as r -join _albion.hole as h on h.id=r.hole_id -join _albion.collar as c on c.id=h.collar_id -join _albion.section as s on st_intersects(s.geom, c.geom) -; - -create view albion.current_facies_section as -select row_number() over() as id, r.hole_id as hole_id, h.collar_id, r.code, r.comments, s.id as section_id, - (albion.to_section(r.geom, s.anchor, s.scale))::geometry('LINESTRING', $SRID) as geom -from _albion.facies as r -join _albion.hole as h on h.id=r.hole_id -join _albion.collar as c on c.id=h.collar_id -join _albion.section as s on st_intersects(s.geom, c.geom) -; create or replace function albion.section_at_group(section_id_ varchar, group_id_ integer) returns geometry @@ -782,13 +658,13 @@ $$ begin return ( with hull as ( - select st_unaryunion(st_collect(c.geom)) as geom + select st_multi(st_unaryunion(st_collect(c.geom))) as geom from _albion.cell as c join _albion.group_cell as gc on gc.cell_id=c.id where gc.section_id=section_id_ and gc.group_id <= group_id_ ), hull_contour as ( - select st_exteriorring(geom) as geom from hull + select st_exteriorring(geom) as geom from (select (st_dump(geom)).geom from hull) as t ), seg as ( select ST_PointN(geom, generate_series(1, ST_NPoints(geom)-1)) as sp, ST_PointN(geom, generate_series(2, ST_NPoints(geom) )) as ep @@ -925,61 +801,14 @@ $$ ito_ -= OC+IC-1 if ifrom_ >= 0 and ito_ > 0 and c: accu = numpy.sum(AVP[ifrom_:ito_]) - grade = accu/(ito_ - ifrom_) oc = (ito_ - ifrom_)*measure_thickness + grade = accu/oc result.append((cut, ifrom_*measure_thickness + first_from_, ito_*measure_thickness + first_from_, oc, accu, grade)) return result $$ ; -select hole_id, (t.r).level_, (t.r).from_, (t.r).to_, (t.r).oc, (t.r).accu, (t.r).grade -from ( -select hole_id, albion.segmentation( - array_agg(gamma order by from_),array_agg(from_ order by from_), array_agg(to_ order by from_), - 1., 1., 10) as r -from _albion.radiometry -group by hole_id -) as t -; - -create table albion.test_mineralization( - id varchar primary key default _albion.unique_id()::varchar, - hole_id varchar not null references _albion.hole(id) on delete cascade on update cascade, - level_ real, - from_ real, - to_ real, - oc real, - accu real, - grade real, - comments varchar, - geom geometry('LINESTRINGZ', $SRID)) -; - -insert into albion.test_mineralization(hole_id, level_, from_, to_, oc, accu, grade) -select hole_id, (t.r).level_, (t.r).from_, (t.r).to_, (t.r).oc, (t.r).accu, (t.r).grade -from ( -select hole_id, albion.segmentation( - array_agg(gamma order by from_),array_agg(from_ order by from_), array_agg(to_ order by from_), - 1., 1., 10) as r -from _albion.radiometry -group by hole_id -) as t -; - -update albion.test_mineralization set geom=albion.hole_piece(from_, to_, hole_id) -; - -create view albion.current_test_mineralization_section as -select row_number() over() as id, m.hole_id, h.collar_id, m.level_, m.oc, m.accu, m.grade, s.id as section_id, - (albion.to_section(m.geom, s.anchor, s.scale))::geometry('LINESTRING', $SRID) as geom -from albion.test_mineralization as m -join _albion.hole as h on h.id=m.hole_id -join _albion.collar as c on c.id=h.collar_id -join _albion.section as s on st_intersects(s.geom, c.geom) -; - - create materialized view albion.all_edge as select case when a < b then a else b end as start_, case when a < b then b else a end as end_ from _albion.cell @@ -991,54 +820,64 @@ select case when c < a then c else a end as start_, case when c < a then a else from _albion.cell ; -create view albion.possible_edge as +create or replace view albion.possible_edge as with tan_ang as ( select tan(correlation_angle*pi()/180) as value, tan(parent_correlation_angle*pi()/180) as parent_value - from _albion.metadata ), result as ( -select ns.id as start_, ne.id as end_, ns.graph_id as graph_id, (st_makeline(st_3dlineinterpolatepoint(ns.geom, .5), st_3dlineinterpolatepoint(ne.geom, .5)))::geometry('LINESTRINGZ', $SRID) as geom, null as parent +select ns.id as start_, ne.id as end_, ns.graph_id as graph_id, (st_makeline(st_3dlineinterpolatepoint(ns.geom, .5), st_3dlineinterpolatepoint(ne.geom, .5)))::geometry('LINESTRINGZ', $SRID) as geom --, null as parent + from albion.all_edge as e -join _albion.hole as hs on hs.collar_id=e.start_ -join _albion.hole as he on he.collar_id=e.end_ +join _albion.hole as hs on hs.id=e.start_ +join _albion.hole as he on he.id=e.end_ join _albion.node as ns on ns.hole_id=hs.id join _albion.node as ne on ne.hole_id=he.id, tan_ang where ns.graph_id = ne.graph_id -and abs(st_z(st_3dlineinterpolatepoint(ns.geom, .5))-st_z(st_3dlineinterpolatepoint(ne.geom,.5))) - /st_distance(st_3dlineinterpolatepoint(ns.geom, .5), st_3dlineinterpolatepoint(ne.geom, .5)) < tan_ang.value +and ( + ( + abs(ns.from_-ns.to_) >= abs(ne.from_-ne.to_) + and st_z(st_startpoint(ns.geom)) + st_distance(st_startpoint(ns.geom), st_startpoint(ne.geom))*tan_ang.value >= st_z(st_startpoint(ne.geom)) + and st_z(st_endpoint(ns.geom)) - st_distance(st_startpoint(ns.geom), st_startpoint(ne.geom))*tan_ang.value <= st_z(st_endpoint(ne.geom)) + ) + or + ( + abs(ns.from_-ns.to_) < abs(ne.from_-ne.to_) + and st_z(st_startpoint(ne.geom)) + st_distance(st_startpoint(ns.geom), st_startpoint(ne.geom))*tan_ang.value >= st_z(st_startpoint(ns.geom)) + and st_z(st_endpoint(ne.geom)) - st_distance(st_startpoint(ns.geom), st_startpoint(ne.geom))*tan_ang.value <= st_z(st_endpoint(ns.geom)) + ) + ) + and st_distance( ne.geom, ns.geom ) < ( select correlation_distance from albion.metadata ) and ns.parent is null and ne.parent is null -union all +union all -- for graphs with parents -select ns.id as start_, ne.id as end_, ns.graph_id as graph_id, (st_makeline(st_3dlineinterpolatepoint(ns.geom, .5), st_3dlineinterpolatepoint(ne.geom, .5)))::geometry('LINESTRINGZ', $SRID) as geom, ns.parent as parent +select ns.id as start_, ne.id as end_, ns.graph_id as graph_id, (st_makeline(st_3dlineinterpolatepoint(ns.geom, .5), st_3dlineinterpolatepoint(ne.geom, .5)))::geometry('LINESTRINGZ', $SRID) as geom --, ns.parent as parent from _albion.edge as pe join _albion.node as pns on pns.id=pe.start_ join _albion.node as pne on pne.id=pe.end_ join _albion.node as ns on ns.parent=pns.id join _albion.node as ne on ne.parent=pne.id, tan_ang where ns.graph_id = ne.graph_id -and abs( -- tan(A-B) = (tan(A)-tan(B))/(1+tan(A)tan(B) +and + ( ( - (st_z(st_3dlineinterpolatepoint(ns.geom, .5))-st_z(st_3dlineinterpolatepoint(ne.geom,.5))) - /st_distance(st_3dlineinterpolatepoint(ns.geom, .5), st_3dlineinterpolatepoint(ne.geom, .5)) - - - (st_z(st_3dlineinterpolatepoint(pns.geom, .5))-st_z(st_3dlineinterpolatepoint(pne.geom,.5))) - /st_distance(st_3dlineinterpolatepoint(pns.geom, .5), st_3dlineinterpolatepoint(pne.geom, .5)) + abs(ns.from_-ns.to_) >= abs(ne.from_-ne.to_) + and st_z(st_startpoint(ns.geom)) + st_distance(st_startpoint(ns.geom), st_startpoint(ne.geom))*tan_ang.parent_value + (st_z(st_3dlineinterpolatepoint(pne.geom, .5)) - st_z(st_3dlineinterpolatepoint(pns.geom, .5))) >= st_z(st_startpoint(ne.geom)) + and st_z(st_endpoint(ns.geom)) - st_distance(st_startpoint(ns.geom), st_startpoint(ne.geom))*tan_ang.parent_value + (st_z(st_3dlineinterpolatepoint(pne.geom, .5)) - st_z(st_3dlineinterpolatepoint(pns.geom, .5))) <= st_z(st_endpoint(ne.geom)) + ) - / + or ( - 1.0 - + - (st_z(st_3dlineinterpolatepoint(ns.geom, .5))-st_z(st_3dlineinterpolatepoint(ne.geom,.5))) - /st_distance(st_3dlineinterpolatepoint(ns.geom, .5), st_3dlineinterpolatepoint(ne.geom, .5)) - * - (st_z(st_3dlineinterpolatepoint(pns.geom, .5))-st_z(st_3dlineinterpolatepoint(pne.geom,.5))) - /st_distance(st_3dlineinterpolatepoint(pns.geom, .5), st_3dlineinterpolatepoint(pne.geom, .5)) + abs(ns.from_-ns.to_) < abs(ne.from_-ne.to_) + and st_z(st_startpoint(ne.geom)) + st_distance(st_startpoint(ns.geom), st_startpoint(ne.geom))*tan_ang.parent_value + (st_z(st_3dlineinterpolatepoint(pns.geom, .5)) - st_z(st_3dlineinterpolatepoint(pne.geom, .5))) >= st_z(st_startpoint(ns.geom)) + + and st_z(st_endpoint(ne.geom)) - st_distance(st_startpoint(ns.geom), st_startpoint(ne.geom))*tan_ang.parent_value + (st_z(st_3dlineinterpolatepoint(pns.geom, .5)) - st_z(st_3dlineinterpolatepoint(pne.geom, .5)) ) <= st_z(st_endpoint(ns.geom)) + + ) ) - ) < tan_ang.parent_value ) select row_number() over() as id, * from result ; @@ -1065,11 +904,11 @@ $$ if new.start_ > new.end_ then select new.start_, new.end_ into new.end_, new.start_; end if; - -- @todo check that edge is in all_edge + -- check that edge is in all_edge select count(1) from albion.all_edge as ae - join _albion.hole as hs on hs.collar_id=ae.start_ - join _albion.hole as he on he.collar_id=ae.end_ + join _albion.hole as hs on hs.id=ae.start_ + join _albion.hole as he on he.id=ae.end_ join _albion.node as ns on (ns.hole_id in (hs.id, he.id) and ns.id=new.start_) join _albion.node as ne on (ne.hole_id in (hs.id, he.id) and ne.id=new.end_) into edge_ok; @@ -1103,11 +942,11 @@ create trigger edge_instead_trig for each row execute procedure albion.edge_instead_fct() ; -create view albion.current_edge_section as -with collar_idx as ( - select c.id, rank() over(partition by s.id order by st_linelocatepoint(s.geom, c.geom)) as rk, c.geom, s.id as section_id +create view albion.edge_section as +with hole_idx as ( + select h.id, rank() over(partition by s.id order by st_linelocatepoint(s.anchor, st_startpoint(h.geom))) as rk, s.id as section_id from _albion.section as s - join _albion.collar as c on st_intersects(s.geom, c.geom) and st_intersects(s.geom, c.geom) + join _albion.hole as h on s.geom && h.geom and st_intersects(s.geom, st_startpoint(h.geom)) ) select s.id || ' ' || e.id as id, e.id as edge_id, e.start_, e.end_, e.graph_id, s.id as section_id, (albion.to_section(e.geom, s.anchor, s.scale))::geometry('LINESTRING', $SRID) as geom @@ -1116,15 +955,17 @@ join _albion.node as ns on ns.id=e.start_ join _albion.node as ne on ne.id=e.end_ join _albion.hole as hs on hs.id=ns.hole_id join _albion.hole as he on he.id=ne.hole_id -join collar_idx as cs on cs.id=hs.collar_id -join collar_idx as ce on ce.id=he.collar_id, +join hole_idx as cs on cs.id=hs.id +join hole_idx as ce on ce.id=he.id, _albion.section as s where ((cs.rk = ce.rk + 1) or (ce.rk = cs.rk + 1)) and cs.section_id=s.id and ce.section_id=s.id ; -create or replace function albion.current_edge_section_instead_fct() +alter view albion.edge_section alter column id set default _albion.unique_id(); + +create or replace function albion.edge_section_instead_fct() returns trigger language plpgsql as @@ -1133,13 +974,13 @@ $$ new_geom geometry; begin if tg_op in ('INSERT', 'UPDATE') then - new.start_ := coalesce(new.start_, (select node_id from albion.current_node_section as n, _albion.metadata as m + new.start_ := coalesce(new.start_, (select node_id from albion.node_section as n, _albion.metadata as m where st_dwithin(n.geom, st_startpoint(new.geom), m.snap_distance) and graph_id=new.graph_id order by st_distance(n.geom, st_startpoint(new.geom)) asc limit 1 )); - new.end_ := coalesce(new.end_, (select node_id from albion.current_node_section as n, _albion.metadata as m + new.end_ := coalesce(new.end_, (select node_id from albion.node_section as n, _albion.metadata as m where st_dwithin(n.geom, st_endpoint(new.geom), m.snap_distance) and graph_id=new.graph_id order by st_distance(n.geom, st_endpoint(new.geom)) asc @@ -1152,6 +993,16 @@ $$ select st_makeline(st_3dlineinterpolatepoint(s.geom, .5), st_3dlineinterpolatepoint(e.geom, .5)) from _albion.node as s, _albion.node as e where s.id=new.start_ and e.id=new.end_ into new_geom; + + -- test if edge is possible + if not exists (select 1 + from albion.all_edge as ae + join _albion.node as ns on ae.start_ = ns.hole_id + join _albion.node as ne on ae.end_ = ne.hole_id + where ns.id = new.start_ and ne.id = new.end_) then + raise 'impossible edge'; + end if; + end if; if tg_op = 'INSERT' then @@ -1171,27 +1022,32 @@ $$ $$ ; -create trigger current_edge_section_instead_trig - instead of insert or update or delete on albion.current_edge_section - for each row execute procedure albion.current_edge_section_instead_fct() +create trigger edge_section_instead_trig + instead of insert or update or delete on albion.edge_section + for each row execute procedure albion.edge_section_instead_fct() ; -create view albion.current_possible_edge_section as +create view albion.possible_edge_section as select row_number() over() as id, e.start_, e.end_, e.graph_id, s.id as section_id, - (albion.to_section(e.geom, s.anchor, s.scale))::geometry('LINESTRING', $SRID) as geom, e.parent + (albion.to_section(e.geom, s.anchor, s.scale))::geometry('LINESTRING', $SRID) as geom --, e.parent from albion.possible_edge as e join _albion.node as ns on ns.id=e.start_ join _albion.node as ne on ne.id=e.end_ join _albion.hole as hs on hs.id=ns.hole_id join _albion.hole as he on he.id=ne.hole_id -join _albion.collar as cs on cs.id=hs.collar_id -join _albion.collar as ce on ce.id=he.collar_id -join _albion.section as s on st_intersects(s.geom, cs.geom) and st_intersects(s.geom, ce.geom) +join _albion.section as s on s.geom && hs.geom and st_intersects(s.geom, st_startpoint(hs.geom)) and + s.geom && he.geom and st_intersects(s.geom, st_startpoint(he.geom)) ; +create type albion.volume_row as ( + geom geometry('MULTIPOLYGONZ', $SRID), + face1 geometry('MULTIPOLYGONZ', $SRID), + face2 geometry('MULTIPOLYGONZ', $SRID), + face3 geometry('MULTIPOLYGONZ', $SRID)) +; -create or replace function albion.elementary_volumes(cell_id_ varchar, graph_id_ varchar, geom_ geometry, holes_ varchar[], starts_ varchar[], ends_ varchar[], hole_ids_ varchar[], node_ids_ varchar[], nodes_ geometry[], end_ids_ varchar[], end_geoms_ geometry[]) -returns setof geometry +create or replace function albion.elementary_volumes(cell_id_ varchar, graph_id_ varchar, geom_ geometry, holes_ varchar[], starts_ varchar[], ends_ varchar[], hole_ids_ varchar[], node_ids_ varchar[], nodes_ geometry[], end_ids_ varchar[], end_geoms_ geometry[], end_holes_ varchar[], end_node_relative_distance real, end_node_relative_thickness real) +returns setof albion.volume_row language plpython3u immutable as $$ @@ -1206,17 +1062,37 @@ $$ # ' '.join(node_ids_)+'\n'+ # ' '.join(nodes_)+'\n'+ # ' '.join(end_ids_)+'\n'+ -# ' '.join(end_geoms_)+'\n' +# ' '.join(end_geoms_)+'\n'+ +# ' '.join(end_holes_)+'\n' #) $INCLUDE_ELEMENTARY_VOLUME -for v in elementary_volumes(holes_, starts_, ends_, hole_ids_, node_ids_, nodes_, end_ids_, end_geoms_, $SRID): - yield v +for g, f1, f2, f3 in elementary_volumes(holes_, starts_, ends_, hole_ids_, node_ids_, nodes_, end_ids_, end_geoms_, end_holes_, $SRID, end_node_relative_distance, end_node_relative_thickness): + yield g, f1, f2, f3 $$ ; -create or replace view albion.volume as -select id, graph_id, cell_id, triangulation -from _albion.volume +create or replace function albion.volume_of_geom(multipoly geometry) +returns real +language plpython3u immutable +as +$$ + from shapely import wkb + import plpy + from numpy import array, average + + m = wkb.loads(bytes.fromhex(multipoly)) + volume = 0 + for p in m: + r = p.exterior.coords + v210 = r[2][0]*r[1][1]*r[0][2]; + v120 = r[1][0]*r[2][1]*r[0][2]; + v201 = r[2][0]*r[0][1]*r[1][2]; + v021 = r[0][0]*r[2][1]*r[1][2]; + v102 = r[1][0]*r[0][1]*r[2][2]; + v012 = r[0][0]*r[1][1]*r[2][2]; + volume += (1./6.)*(-v210 + v120 + v201 - v021 - v102 + v012) + return volume +$$ ; create or replace function albion.is_closed_volume(multipoly geometry) @@ -1239,6 +1115,11 @@ $$ $$ ; +create or replace view albion.volume as +select id, graph_id, cell_id, triangulation, albion.volume_of_geom(triangulation) as volume--, albion.is_closed_volume(triangulation) as is_closed +from _albion.volume +; + create or replace function albion.mesh_boundarie(multipoly geometry) returns geometry language plpython3u immutable @@ -1264,30 +1145,6 @@ $$ $$ ; -create or replace function albion.volume_of_geom(multipoly geometry) -returns real -language plpython3u immutable -as -$$ - from shapely import wkb - import plpy - from numpy import array, average - - m = wkb.loads(bytes.fromhex(multipoly)) - volume = 0 - for p in m: - r = p.exterior.coords - v210 = r[2][0]*r[1][1]*r[0][2]; - v120 = r[1][0]*r[2][1]*r[0][2]; - v201 = r[2][0]*r[0][1]*r[1][2]; - v021 = r[0][0]*r[2][1]*r[1][2]; - v102 = r[1][0]*r[0][1]*r[2][2]; - v012 = r[0][0]*r[1][1]*r[2][2]; - volume += (1./6.)*(-v210 + v120 + v201 - v021 - v102 + v012) - return volume -$$ -; - create or replace function albion.volume_union(multipoly geometry) returns geometry @@ -1434,18 +1291,18 @@ $$ ; create view albion.end_node as -select id, geom, node_id, collar_id, graph_id +select id, geom, node_id, hole_id, graph_id from _albion.end_node ; -- view of termination edges create or replace view albion.half_edge as -select n.id as node_id, n.graph_id, h.collar_id, case when ae.start_=h.collar_id then ae.end_ else ae.start_ end as other +select n.id as node_id, n.graph_id, h.id as hole_id, case when ae.start_=h.id then ae.end_ else ae.start_ end as other from _albion.node as n join _albion.hole as h on h.id=n.hole_id -join albion.all_edge as ae on (ae.start_=h.collar_id or ae.end_=h.collar_id) +join albion.all_edge as ae on (ae.start_=h.id or ae.end_=h.id) except -select n.id, n.graph_id, h.collar_id, case when e.start_=n.id then he.collar_id else hs.collar_id end as other +select n.id, n.graph_id, h.id as hole_id, case when e.start_=n.id then he.id else hs.id end as other from _albion.node as n join _albion.hole as h on h.id=n.hole_id join _albion.edge as e on (e.start_=n.id or e.end_=n.id) @@ -1455,50 +1312,74 @@ join _albion.hole as hs on hs.id=ns.hole_id join _albion.hole as he on he.id=ne.hole_id ; -create function albion.end_node_geom(node_geom_ geometry, collar_geom_ geometry) +create or replace function albion.end_node_geom(node_geom_ geometry, collar_geom_ geometry, rel_distance real default .3, rel_thickness real default .3, nx real default null, ny real default null, nz real default null) returns geometry language plpython3u as $$ from numpy import array + from numpy import cross from shapely import wkb from shapely.geometry import LineString from shapely import geos + from math import sqrt geos.WKBWriter.defaults['include_srid'] = True - REL_DISTANCE = .3 - HEIGHT = 1. - node_geom = wkb.loads(bytes.fromhex(node_geom_)) collar_geom = wkb.loads(bytes.fromhex(collar_geom_)) node_coords = array(node_geom.coords) + thickness = rel_thickness*abs(node_coords[0][2] - node_coords[1][2]) center = .5*(node_coords[0] + node_coords[1]) dir = array(collar_geom.coords[0]) - center dir[2] = 0 - dir *= REL_DISTANCE - top = center + dir + array([0,0,.5*HEIGHT]) - bottom = center + dir - array([0,0,.5*HEIGHT]) + dir *= rel_distance + dir = cross(array([nx, ny, nz]), cross(dir, array([0,0,1]))) + top = center + dir + array([0,0,.5*thickness]) + bottom = center + dir - array([0,0,.5*thickness]) result = LineString([tuple(top), tuple(bottom)]) + geos.lgeos.GEOSSetSRID(result._geom, geos.lgeos.GEOSGetSRID(node_geom._geom)) return result.wkb_hex $$ ; -create view albion.dynamic_end_node as -select row_number() over() as id, he.graph_id, n.id as node_id, albion.end_node_geom(n.geom, c.geom)::geometry('LINESTRINGZ', $SRID) as geom, c.id as collar_id +create or replace view albion.normal as +select e.id, e.start_, e.end_, + - (st_x(st_endpoint(geom)) - st_x(st_startpoint(geom))) * ((st_z(st_endpoint(geom)) - st_z(st_startpoint(geom)))) as nx, + - (st_y(st_endpoint(geom)) - st_y(st_startpoint(geom))) * ((st_z(st_endpoint(geom)) - st_z(st_startpoint(geom)))) as ny, + (st_x(st_endpoint(geom)) - st_x(st_startpoint(geom)))^2 + ((st_y(st_endpoint(geom)) - st_y(st_startpoint(geom))))^2 as nz, + (180/pi())*atan(abs(st_z(st_endpoint(geom)) - st_z(st_startpoint(geom)))/st_length(geom)) as angl +from _albion.edge as e +; + +create or replace view albion.average_normal as +with nrml as ( + select avg(nx) as nx, avg(ny) as ny, avg(nz) as nz, n.id + from _albion.node as n + left join albion.normal as e on e.start_=n.id or e.end_=n.id + group by n.id +) +select id, coalesce(nx/sqrt(nx^2+ny^2+nz^2), 0) as nx, coalesce(ny/sqrt(nx^2+ny^2+nz^2), 0) as ny, coalesce(nz/sqrt(nx^2+ny^2+nz^2), 1) as nz +from nrml +; + + +create or replace view albion.dynamic_end_node as +select row_number() over() as id, he.graph_id, n.id as node_id, albion.end_node_geom(n.geom, st_startpoint(h.geom), m.end_node_relative_distance, m.end_node_relative_thickness, nrml.nx::real, nrml.ny::real, nrml.nz::real)::geometry('LINESTRINGZ', $SRID) as geom, h.id as hole_id from albion.half_edge as he join _albion.node as n on n.id=he.node_id join _albion.hole as h on h.id=he.other -join _albion.collar as c on c.id=h.collar_id +join _albion.metadata as m on 't' +join albion.average_normal as nrml on nrml.id = coalesce(n.parent, n.id); ; -create view albion.current_end_node_section as -with collar_idx as ( - select c.id, rank() over(partition by s.id order by st_linelocatepoint(s.geom, c.geom)) as rk, c.id as collar_id, c.geom, s.id as section_id +create or replace view albion.end_node_section as +with hole_idx as ( + select h.id, rank() over(partition by s.id order by st_linelocatepoint(s.anchor, st_startpoint(h.geom))) as rk, h.id as hole_id, s.id as section_id from _albion.section as s - join _albion.collar as c on st_intersects(s.geom, c.geom) + join _albion.hole as h on s.geom && h.geom and st_intersects(s.geom, st_startpoint(h.geom)) ) select tn.id||' '||s.id as id, tn.id as end_node_id, n.id as node_id, tn.graph_id, s.id as section_id, (albion.to_section(tn.geom, s.anchor, s.scale))::geometry('LINESTRING', $SRID) as geom, @@ -1507,12 +1388,12 @@ from _albion.end_node as tn join _albion.node as n on n.id=tn.node_id join _albion.hole as h on h.id=n.hole_id join _albion.section as s on true -join collar_idx as cn on (cn.collar_id=h.collar_id and cn.section_id=s.id) -join collar_idx as cc on (cc.collar_id=tn.collar_id and cc.section_id=s.id) +join hole_idx as cn on (cn.id=h.id and cn.section_id=s.id) +join hole_idx as cc on (cc.id=tn.hole_id and cc.section_id=s.id) where cn.rk=cc.rk+1 or cc.rk=cn.rk+1 ; -create or replace function albion.current_end_node_section_instead_fct() +create or replace function albion.end_node_section_instead_fct() returns trigger language plpgsql as @@ -1541,33 +1422,31 @@ $$ $$ ; -create trigger current_end_node_section_instead_trig - instead of insert or update or delete on albion.current_end_node_section - for each row execute procedure albion.current_end_node_section_instead_fct() +create trigger end_node_section_instead_trig + instead of insert or update or delete on albion.end_node_section + for each row execute procedure albion.end_node_section_instead_fct() ; -create or replace view albion.current_section_polygon as +create or replace view albion.section_polygon as with node as ( - select node_id, section_id, geom from albion.current_node_section + select node_id, section_id, geom from albion.node_section ), edge as ( - select graph_id, section_id, start_, end_ from albion.current_edge_section + select graph_id, section_id, start_, end_ from albion.edge_section ), poly as ( - select st_union( + select ('SRID=$SRID; POLYGON(('|| st_x(st_startpoint(ns.geom)) ||' '||st_y(st_startpoint(ns.geom))||','|| st_x(st_endpoint(ns.geom)) ||' '||st_y(st_endpoint(ns.geom)) ||','|| st_x(st_endpoint(ne.geom)) ||' '||st_y(st_endpoint(ne.geom)) ||','|| st_x(st_startpoint(ne.geom)) ||' '||st_y(st_startpoint(ne.geom))||','|| st_x(st_startpoint(ns.geom)) ||' '||st_y(st_startpoint(ns.geom))|| - '))')::geometry - ) as geom, e.graph_id, e.section_id + '))')::geometry as geom, e.graph_id, e.section_id from edge as e join node as ns on ns.node_id=e.start_ and ns.section_id=e.section_id join node as ne on ne.node_id=e.end_ and ne.section_id=e.section_id - group by e.graph_id, e.section_id ), term as ( select ('SRID=$SRID; POLYGON(('|| @@ -1578,16 +1457,17 @@ term as ( st_x(st_startpoint(t.node_geom)) ||' '||st_y(st_startpoint(t.node_geom))|| '))')::geometry as geom, t.graph_id, t.section_id - from albion.current_end_node_section as t + from albion.end_node_section as t ) -select row_number() over() as id, geom::geometry('POLYGON', $SRID), graph_id, section_id -from (select * from poly union all select * from term) as t +select row_number() over() as id, st_multi(st_union(geom))::geometry('MULTIPOLYGON', $SRID) as geom, graph_id, section_id +from (select * from poly union all select * from term where st_isvalid(geom)) as t +group by graph_id, section_id ; -create or replace view albion.current_section_intersection as +create or replace view albion.section_intersection as with inter as ( select st_collectionextract((st_dump(st_intersection(a.geom, b.geom))).geom, 3) as geom - from albion.current_section_polygon as a, albion.current_section_polygon as b + from albion.section_polygon as a, albion.section_polygon as b where a.id>b.id and a.graph_id=b.graph_id and a.section_id=b.section_id @@ -1604,12 +1484,12 @@ where not st_isempty(geom) create or replace view albion.dynamic_volume as with res as ( select -c.id as cell_id, g.id as graph_id, ed.starts, ed.ends, nd.hole_ids as hole_ids, nd.ids as node_ids, nd.geoms as node_geoms, en.ids as end_ids, en.geoms as end_geoms, c.geom, ARRAY[ha.id, hb.id, hc.id] as holes +c.id as cell_id, g.id as graph_id, ed.starts, ed.ends, nd.hole_ids as hole_ids, nd.ids as node_ids, nd.geoms as node_geoms, en.ids as end_ids, en.geoms as end_geoms, c.geom, ARRAY[ha.id, hb.id, hc.id] as holes, en.end_holes from _albion.graph as g join _albion.cell as c on true -join _albion.hole as ha on ha.collar_id = c.a -join _albion.hole as hb on hb.collar_id = c.b -join _albion.hole as hc on hc.collar_id = c.c +join _albion.hole as ha on ha.id = c.a +join _albion.hole as hb on hb.id = c.b +join _albion.hole as hc on hc.id = c.c join lateral ( select coalesce(array_agg(n.id), '{}'::varchar[]) as ids, coalesce(array_agg(n.hole_id), '{}'::varchar[]) as hole_ids, coalesce(array_agg(n.geom), '{}'::geometry[]) as geoms from _albion.node as n @@ -1625,16 +1505,22 @@ join lateral ( and e.graph_id=g.id ) as ed on true join lateral ( - select coalesce(array_agg(en.node_id), '{}'::varchar[]) as ids, coalesce(array_agg(en.geom), '{}'::geometry[]) as geoms + select coalesce(array_agg(en.node_id), '{}'::varchar[]) as ids, coalesce(array_agg(en.geom), '{}'::geometry[]) as geoms, coalesce(array_agg(en.hole_id), '{}'::varchar[]) as end_holes from _albion.end_node as en join _albion.node as n on n.id=en.node_id - where en.collar_id in (c.a, c.b, c.c) + where en.hole_id in (c.a, c.b, c.c) and n.hole_id in (ha.id, hb.id, hc.id) and en.graph_id=g.id ) as en on true ) -select cell_id, graph_id, albion.elementary_volumes(cell_id, graph_id, st_force3d(geom), holes, starts, ends, hole_ids, node_ids, node_geoms, end_ids, end_geoms)::geometry('MULTIPOLYGONZ', $SRID) as geom, starts, ends, holes, hole_ids, node_ids, end_ids, end_geoms -from res +select cell_id, graph_id, + t.geom::geometry('MULTIPOLYGONZ', $SRID), + t.face1::geometry('MULTIPOLYGONZ', $SRID), + t.face2::geometry('MULTIPOLYGONZ', $SRID), + t.face3::geometry('MULTIPOLYGONZ', $SRID), + starts, ends, holes, hole_ids, node_ids, end_ids, end_geoms +from res, albion.metadata m +join lateral albion.elementary_volumes(cell_id, graph_id, st_force3d(geom), holes, starts, ends, hole_ids, node_ids, node_geoms, end_ids, end_geoms, end_holes, m.end_node_relative_distance, m.end_node_relative_thickness) as t on true ; @@ -1679,3 +1565,219 @@ from res --'01050000A0787F00000E00000001020000800200000013154A5788BC1341F27E88ADD2A93F41DDC836A48ECB7640F78772A388BC13411BFEBE9FD2A93F41C0A2699C9393764001020000800200000059D97C6C85BC13418A67D48DBAA93F41FF7B08EA919F7640418F5F6685BC1341B0A9D78CBAA93F4178920C2DC58A764001020000800200000013154A5788BC1341F27E88ADD2A93F41DDC836A48ECB7640F78772A388BC13411BFEBE9FD2A93F41C0A2699C939376400102000080020000005E3F399385BC13419833B790BAA93F412744AD66C4EA764050BFC17285BC1341BB98A18EBAA93F4131BD49D291AF764001020000800200000013154A5788BC1341F27E88ADD2A93F41DDC836A48ECB7640F78772A388BC13411BFEBE9FD2A93F41C0A2699C93937640010200008002000000EAF7DBB529BC1341350EF10DD4A93F415EECEAE169E176400A74A55E2ABC1341E896ED28D4A93F4169719E7FAD947640010200008002000000EAF7DBB529BC1341350EF10DD4A93F415EECEAE169E176400A74A55E2ABC1341E896ED28D4A93F4169719E7FAD94764001020000800200000059D97C6C85BC13418A67D48DBAA93F41FF7B08EA919F7640418F5F6685BC1341B0A9D78CBAA93F4178920C2DC58A7640010200008002000000EAF7DBB529BC1341350EF10DD4A93F415EECEAE169E176400A74A55E2ABC1341E896ED28D4A93F4169719E7FAD9476400102000080020000005E3F399385BC13419833B790BAA93F412744AD66C4EA764050BFC17285BC1341BB98A18EBAA93F4131BD49D291AF764001020000800200000056B7652688BC1341EA1D85B9D2A93F41D7DF7D1858F87640E1BD403F88BC13418DABDEB2D2A93F41BFCC2B28C0DE76400102000080020000005E3F399385BC13419833B790BAA93F412744AD66C4EA764050BFC17285BC1341BB98A18EBAA93F4131BD49D291AF764001020000800200000056B7652688BC1341EA1D85B9D2A93F41D7DF7D1858F87640E1BD403F88BC13418DABDEB2D2A93F41BFCC2B28C0DE764001020000800200000042B5BD4C29BC134102FDA4FBD3A93F4171017AF02C16774018C4BC7729BC134173507003D4A93F4124AB1F80CAFF7640'::geometry)) --; +-- collect triangles of neighbor elementary volumes +create or replace function albion.triangle_intersection(t1_ geometry, t2_ geometry) +returns geometry +language plpython3u immutable +as +$$ + from shapely import geos + from shapely.geometry import MultiPolygon, Polygon + from shapely import wkb + import numpy + import plpy + geos.WKBWriter.defaults['include_srid'] = True + t1 = wkb.loads(bytes.fromhex(t1_)) + t2 = wkb.loads(bytes.fromhex(t2_)) + + t1s = set((tuple(t.exterior.coords[0:3]) for t in t1)) + t2s = set((tuple(reversed(t.exterior.coords[0:3])) for t in t2)) + + result = MultiPolygon([ Polygon(t) for t in t1s.intersection(t2s)]) + if not len(result): + return None + geos.lgeos.GEOSSetSRID(result._geom, geos.lgeos.GEOSGetSRID(t1._geom)) + return result.wkb_hex +$$ +; + +--create or replace function albion.triangle_intersection(t1_ geometry, t2_ geometry) +--returns geometry +--language plpgsql immutable +--as +--$$ +-- begin +-- return (select st_collect(geom) from +-- ( select a.geom +-- from st_dump(t1_) a, st_dump(t2_) b +-- where a.geom = st_reverse(b.geom) +-- ) as t); +-- +-- end; +--$$ +--; + + +--create or replace view albion.volume_section as +--with touching_cell as ( +-- select c.id, st_intersection(s.geom, c.geom) as geom, v.triangulation, s.id as section_id, v.graph_id +-- from _albion.section as s +-- join _albion.cell as c on c.geom && s.geom +-- and st_intersects(c.geom, s.geom) +-- and st_length(st_intersection(s.geom, c.geom)) > 0 +-- join _albion.volume as v on v.cell_id = c.id +--), +--tri as ( +-- select (st_dump(albion.triangle_intersection(c1.triangulation, c2.triangulation))).geom as geom, c1.section_id, c1.graph_id +-- from touching_cell as c1 +-- join touching_cell as c2 on c2.id > c1.id and st_length(st_intersection(c1.geom, c2.geom)) >0 and c1.section_id = c2.section_id and c1.graph_id = c2.graph_id +--) +--select row_number() over() as id, st_collect(geom)::geometry('MULTIPOLYGONZ', $SRID) as geom, section_id, graph_id +--from tri +--group by section_id, graph_id +--; + + +create or replace view albion.edge_face as +select t.n[1] as start_, t.n[2] as end_, face1 as geom, graph_id, cell_id, 'face1' as face +from _albion.volume v +join _albion.cell c on c.id = v.cell_id +join lateral (select array_agg(x order by x) as n from (values (a), (b), (c)) as v(x)) as t on true +union all +select t.n[2] as start_, t.n[3] as end_, face2 as geom, graph_id, cell_id, 'face2' as face +from _albion.volume v +join _albion.cell c on c.id = v.cell_id +join lateral (select array_agg(x order by x) as n from (values (a), (b), (c)) as v(x)) as t on true +union all +select t.n[1] as start_, t.n[3] as end_, face3 as geom, graph_id, cell_id, 'face3' as face +from _albion.volume v +join _albion.cell c on c.id = v.cell_id +join lateral (select array_agg(x order by x) as n from (values (a), (b), (c)) as v(x)) as t on true +; + +create view albion.section_edge as +with hole_idx as ( + select s.id as section_id, h.id as hole_id + from _albion.section as s + join _albion.hole as h on s.geom && h.geom and st_intersects(s.geom, st_startpoint(h.geom)) +) +select e.start_, e.end_, hs.section_id +from albion.all_edge as e +join hole_idx as hs on hs.hole_id = e.start_ +join hole_idx as he on he.hole_id = e.end_ and he.section_id = hs.section_id +; + +create or replace view albion.volume_section as +select se.section_id, ef.graph_id, st_collectionhomogenize(st_collect(ef.geom))::geometry('MULTIPOLYGONZ', 32632) as geom +from albion.section_edge as se +join albion.edge_face as ef on ef.start_ = se.start_ and ef.end_ = se.end_ and not st_isempty(ef.geom) +group by se.section_id, ef.graph_id +; + + +create or replace view albion.named_section as +select s.id, s.geom, s.section, rank() over (partition by section order by st_distance(s.geom, a.anchor)) as rank_, s.cut +from _albion.named_section as s +join _albion.section as a on s.section = a.id +; + +create or replace function albion.named_section_instead_fct() +returns trigger +language plpgsql +as +$$ + begin + if tg_op in ('INSERT', 'UPDATE') then + new.id := coalesce(new.id, _albion.unique_id()::varchar); + new.cut := coalesce(new.cut, ( + with geom as ( + select st_dumppoints(new.geom) as pt + ), + segment as ( + select st_makeline(lag((pt).geom) over (order by (pt).path), (pt).geom) as geom from geom + ), + filtered as ( + select geom from segment as s + except + select s.geom from segment as s + join _albion.named_section as o + on st_intersects(o.cut, s.geom) + and st_linelocatepoint(s.geom, st_intersection(o.cut, s.geom)) not in (0.0, 1.0) + and st_geometrytype(st_intersection(o.cut, s.geom)) = 'ST_Point' + ) + select st_multi(st_linemerge(st_collect(geom))) from filtered + )); + end if; + + + if tg_op = 'INSERT' then + insert into _albion.named_section(id, geom, cut, section) + values(new.id, new.geom, new.cut, new.section) + returning id into new.id; + return new; + elsif tg_op = 'UPDATE' then + update _albion.named_section set id=new.id, geom=new.geom, cut=new.cut, section=new.section + where id=old.id; + return new; + elsif tg_op = 'DELETE' then + delete from _albion.named_section where id=old.id; + return old; + end if; + end; +$$ +; + +create trigger named_section_instead_trig + instead of insert or update or delete on albion.named_section + for each row execute procedure albion.named_section_instead_fct() +; + + +create or replace function albion.next_section(section_ varchar) +returns geometry +language plpgsql stable +as +$$ + begin + return ( + select n.cut + from albion.named_section as n + join albion.section as s on s.id=n.section + where s.id=section_ and st_distance(n.cut, s.anchor) > coalesce(st_distance(s.geom, s.anchor), 0) + order by st_distance(n.cut, s.anchor) asc + limit 1 + ); + end; +$$ +; + +create or replace function albion.previous_section(section_ varchar) +returns geometry +language plpgsql stable +as +$$ + begin + return ( + select n.cut + from albion.named_section as n + join albion.section as s on s.id=n.section + where s.id=section_ and st_distance(n.cut, s.anchor) < coalesce(st_distance(s.geom, s.anchor), 9999999) + order by st_distance(n.cut, s.anchor) desc + limit 1 + ); + end; +$$ +; + + +-- TODO +-- [x] ajout de collar stérile (avec note) +-- [x] polygone de maillage convex hull ou un trou +-- supprimer des cellules et les edges associés + +-- Methode +-- import des collar et deviations dans répertoire + option data to_ from_ +-- calcul des minéralisations +-- ajout de collar stériles flmaggés (verticaux) +-- creation des sections nommées +-- triangulation +-- effacer des cellules +-- creation de graph + + + +/* +select (st_dumppoints(st_intersection(a.geom, b.geom))).geom as geom from albion.named_section as a join albion.named_section as b on st_intersects(a.geom, b.geom) and a.id > b.id +except +select st_force2d(geom) as geom from albion.collar +*/ diff --git a/albion_table.sql b/albion_table.sql new file mode 100644 index 0000000..4d40d89 --- /dev/null +++ b/albion_table.sql @@ -0,0 +1,62 @@ +create view albion.${NAME} as +select t.id, t.hole_id, t.from_, t.to_, st_startpoint(h.geom)::geometry('POINTZ', ${SRID}) as geom, ${T_FIELDS} +from _albion.${NAME} as t +join _albion.hole as h on h.id = t.hole_id +; + +alter view albion.${NAME} alter column id set default _albion.unique_id() +; + +create or replace function albion.${NAME}_instead_fct() +returns trigger +language plpgsql +as +$$$$ + begin + if tg_op = 'INSERT' then + insert into _albion.${NAME}(id, hole_id, from_, to_, ${FIELDS}) + values(new.id, new.hole_id, new.from_, new.to_, ${NEW_FIELDS}) + returning id into new.id; + return new; + elsif tg_op = 'UPDATE' then + update _albion.${NAME} set id=new.id, hole_id=new.hole_id, from_=new.from_, to_=new.to_, ${SET_FIELDS} + where id=old.id; + return new; + elsif tg_op = 'DELETE' then + delete from _albion.${NAME} where id=old.id; + return old; + end if; + end; +$$$$ +; + +create trigger ${NAME}_instead_trig + instead of insert or update or delete on albion.${NAME} + for each row execute procedure albion.${NAME}_instead_fct() +; + +create materialized view albion.${NAME}_section_geom_cache as +select s.id as section_id, h.id as hole_id, t.id as ${NAME}_id, + (albion.to_section( + st_makeline(st_3dlineinterpolatepoint(h.geom, least(t.from_/h.depth_, 1)), + st_3dlineinterpolatepoint(h.geom, least(t.to_/h.depth_, 1))) + , s.anchor, s.scale)) as geom, + st_startpoint(h.geom) as collar +from _albion.section as s, _albion.${NAME} as t +join _albion.hole as h on h.id=t.hole_id +; + +create index ${NAME}_section_geom_cache_${NAME}_id_idx on albion.${NAME}_section_geom_cache(${NAME}_id) +; + +create index ${NAME}_section_geom_cache_colar_idx on albion.${NAME}_section_geom_cache using gist(collar) +; + + +create view albion.${NAME}_section as +select row_number() over() as id, t.id as ${NAME}_id, sc.section_id, t.hole_id, sc.geom::geometry('LINESTRING', ${SRID}), ${T_FIELDS} +from _albion.${NAME} as t +join albion.${NAME}_section_geom_cache as sc on sc.${NAME}_id = t.id +join _albion.section as s on st_intersects(s.geom, sc.collar) and sc.section_id = s.id +; + diff --git a/axis_layer.py b/axis_layer.py deleted file mode 100644 index af09dc5..0000000 --- a/axis_layer.py +++ /dev/null @@ -1,75 +0,0 @@ -# coding: utf-8 -from qgis.core import (QgsPluginLayerType, - QgsPluginLayer, - QgsRectangle) - -from PyQt4.QtCore import pyqtSignal - -import traceback -import logging - - -class AxisLayerType(QgsPluginLayerType): - def __init__(self): - QgsPluginLayerType.__init__(self, AxisLayer.LAYER_TYPE) - - def createLayer(self): - return AxisLayer() - return True - - def showLayerProperties(self, layer): - return False - - -class AxisLayer(QgsPluginLayer): - - LAYER_TYPE = 'axis' - - __msg = pyqtSignal(str) - __drawException = pyqtSignal(str) - - def __init__(self, crs): - QgsPluginLayer.__init__( - self, - AxisLayer.LAYER_TYPE, - 'axis plugin layer') - self.__msg.connect(self.__print) - self.__drawException.connect(self.__raise) - self.setCrs(crs) - self.setValid(True) - - def extent(self): - return QgsRectangle(-1, -1, 1, 1) - - def __print(self, msg): - logging.info(msg) - - def __raise(self, err): - logging.error(err) - raise Exception(err) - - def draw(self, rendererContext): - try: - painter = rendererContext.painter() - ext = rendererContext.extent() - map_unit_per_pixel = rendererContext.mapToPixel().\ - mapUnitsPerPixel() - width, height = \ - int((ext.xMaximum()-ext.xMinimum())/map_unit_per_pixel),\ - int((ext.yMaximum()-ext.yMinimum())/map_unit_per_pixel) - nb_div = 10 - dw, dh = width/nb_div, height/nb_div - dx, dy = ((ext.xMaximum()-ext.xMinimum())/nb_div, - (ext.yMaximum()-ext.yMinimum())/nb_div) - - for i in range(nb_div+2): - painter.drawText(5, int(i*dh), "%.0f" % (ext.yMaximum()-i*dy)) - painter.drawLine(50, int(i*dh), 60, int(i*dh)) - for i in range(nb_div+2): - painter.drawText(int(i*dw), 20, "%.0f" % (ext.xMinimum()+i*dx)) - painter.drawLine(int(i*dw), 20, int(i*dw), 25) - - return True - except Exception as e: - self.__drawException.emit(traceback.format_exc(e)) - return False diff --git a/data/nt.zip b/data/nt.zip index 1526b0c..263d071 100644 Binary files a/data/nt.zip and b/data/nt.zip differ diff --git a/doc/__main__.py b/doc/__main__.py index c8988ed..72d7164 100644 --- a/doc/__main__.py +++ b/doc/__main__.py @@ -10,6 +10,7 @@ """ +from builtins import str import sys import getopt from . import build diff --git a/elementary_volume/__init__.py b/elementary_volume/__init__.py index bd014d2..30983eb 100644 --- a/elementary_volume/__init__.py +++ b/elementary_volume/__init__.py @@ -1,4 +1,9 @@ # coding = utf-8 +from builtins import next +from builtins import str +from builtins import zip +from builtins import range +from builtins import object import os import random from itertools import combinations @@ -159,7 +164,7 @@ def linemerge(lines): return merged def has_proper_2d_topology(line): - SIN_MIN_ANGLE = 5.*PI/180 + SIN_MIN_ANGLE = .01*PI/180 l = line if len(l) <=2 : return False @@ -190,21 +195,36 @@ def offset_coords(offsets, coords): return [offsets[c] if c in offsets else c for c in coords] -def elementary_volumes(holes_, starts_, ends_, hole_ids_, node_ids_, nodes_, end_ids_, end_geoms_, srid_=32632): +def elementary_volumes(holes_, starts_, ends_, hole_ids_, node_ids_, nodes_, end_ids_, end_geoms_, end_holes_, srid_=32632, end_node_relative_distance=0.3, end_node_relative_thickness=.3): DEBUG = False - PRECI = 9 - REL_DISTANCE = .3 - HEIGHT = 1. + PRECI = 6 debug_files = [] nodes = {id_: wkb.loads(bytes.fromhex(geom)) for id_, geom in zip(node_ids_, nodes_)} ends = defaultdict(list) - for id_, geom in zip(end_ids_, end_geoms_): + end_holes = defaultdict(list) + for id_, geom, hole_id in zip(end_ids_, end_geoms_, end_holes_): ends[id_].append(wkb.loads(bytes.fromhex(geom))) + end_holes[id_].append(hole_id) holes = {n: h for n, h in zip(node_ids_, hole_ids_)} edges = [(s, e) for s, e in zip(starts_, ends_)] - assert(len(edges) == len(set(edges))) + #assert(len(edges) == len(set(edges))) + #assert(len(holes_) == 3) + #assert(set(hole_ids_).intersection(set(holes_)) == set(hole_ids_)) + #assert(set(end_holes_).intersection(set(holes_)) == set(end_holes_)) + + + # translate everything close to origin to avoid numerical issues + translation = None + for id_ in nodes.keys(): + if translation is None: + translation = nodes[id_].coords[0] + nodes[id_] = translate(nodes[id_], -translation[0], -translation[1], -translation[2]) + + for id_ in ends.keys(): + for i in range(len(ends[id_])): + ends[id_][i] = translate(ends[id_][i], -translation[0], -translation[1], -translation[2]) graph = defaultdict(set) # undirected (edge in both directions) for e in edges: @@ -231,8 +251,6 @@ def elementary_volumes(holes_, starts_, ends_, hole_ids_, node_ids_, nodes_, end triangle_edges.add(tri[0:1]+tri[2:3]) triangle_nodes.update(tri) - result = [] - termination = [] # compute face offset direction for termination corners @@ -240,21 +258,23 @@ def elementary_volumes(holes_, starts_, ends_, hole_ids_, node_ids_, nodes_, end # and that have at leat 2 incident edges that are not in the same # face (i.e. different holes) unused_nodes = set(nodes.keys()).difference(triangle_nodes) - offsets = {nodes[n].coords[0]: ends[n][0].coords[0] for n, l in ends.items() if len(l)==1} - offsets.update({nodes[n].coords[-1]: ends[n][0].coords[-1] for n, l in ends.items() if len(l)==1}) + offsets = {nodes[n].coords[0]: ends[n][0].coords[0] for n, l in list(ends.items()) if len(l)==1} + offsets.update({nodes[n].coords[-1]: ends[n][0].coords[-1] for n, l in list(ends.items()) if len(l)==1}) for n in unused_nodes: p = pair_of_non_coplanar_neighbors(n, graph, holes) if p: A, B, C = array(nodes[n].coords[0][:2]), array(nodes[p[0]].coords[0][:2]),array(nodes[p[1]].coords[0][:2]) c = average(array(nodes[n].coords), (0,)) - u = .5*(normalized(B-A)+normalized(C-A))*REL_DISTANCE*.5*(norm(B-A)+norm(C-A)) - offsets[nodes[n].coords[0]] = tuple(c+array((u[0], u[1], +.5*HEIGHT))) - offsets[nodes[n].coords[-1]] = tuple(c+array((u[0], u[1], -.5*HEIGHT))) + u = .5*(normalized(B-A)+normalized(C-A))*end_node_relative_distance*.5*(norm(B-A)+norm(C-A)) + thickness = abs(nodes[n].coords[0][2] - nodes[n].coords[-1][2]) + end_node_thickness = end_node_relative_thickness*thickness + offsets[nodes[n].coords[0]] = tuple(c+array((u[0], u[1], +.5*end_node_thickness))) + offsets[nodes[n].coords[-1]] = tuple(c+array((u[0], u[1], -.5*end_node_thickness))) if DEBUG: open('/tmp/offsets.vtk', 'w').write( - to_vtk(MultiLineString([n for l in ends.values() for n in l]).wkb_hex)) + to_vtk(MultiLineString([n for l in list(ends.values()) for n in l]).wkb_hex)) sorted_holes = sorted(holes_) # face origin is the lowest bottom of the node in the first hole @@ -262,14 +282,18 @@ def elementary_volumes(holes_, starts_, ends_, hole_ids_, node_ids_, nodes_, end face_idx = -1 lines = [] - for hl, hr in ((sorted_holes[0], sorted_holes[1]), - (sorted_holes[1], sorted_holes[2]), - (sorted_holes[0], sorted_holes[2])): + faces = defaultdict(list) + result = [] + termination = [] + for hl, hr, other_hole in ( + (sorted_holes[0], sorted_holes[1], sorted_holes[2]), + (sorted_holes[1], sorted_holes[2], sorted_holes[0]), + (sorted_holes[0], sorted_holes[2], sorted_holes[1])): face_idx += 1 direct_orientation = (hl, hr) == (holes_[0], holes_[1]) or (hl, hr) == (holes_[1], holes_[2]) or (hl, hr) == (holes_[2], holes_[0]) face_edges = list(set([(s, e) if holes[s] == hl else (e, s) - for s in graph.keys() for e in graph[s] if holes[s] in (hl, hr) and holes[e] in (hl, hr)])) + for s in list(graph.keys()) for e in graph[s] if holes[s] in (hl, hr) and holes[e] in (hl, hr)])) if not len(face_edges): continue @@ -280,15 +304,16 @@ def elementary_volumes(holes_, starts_, ends_, hole_ids_, node_ids_, nodes_, end face_lines.append(Line([nodes[e[0]].coords[1], nodes[e[1]].coords[1]], Line.BOTTOM)) # split lines - for i, j in combinations(range(len(face_lines)), 2): + for i, j in combinations(list(range(len(face_lines))), 2): assert(face_lines[i].side != Line.VERTICAL and face_lines[j].side != Line.VERTICAL) p = sym_split(face_lines[i].points, face_lines[j].points) if p and p not in offsets: if face_lines[i].points[0] in offsets and face_lines[i].points[-1] in offsets\ and face_lines[j].points[0] in offsets and face_lines[j].points[-1] in offsets: - offsets[p] = sym_split( + splt = sym_split( offset_coords(offsets, [face_lines[i].points[0], face_lines[i].points[-1]]), offset_coords(offsets, [face_lines[j].points[0], face_lines[j].points[-1]])) + offsets[p] = splt if splt else p else: offsets[p] = p @@ -302,7 +327,7 @@ def elementary_volumes(holes_, starts_, ends_, hole_ids_, node_ids_, nodes_, end else: offsets[p] = p - for k, n in nodes.items(): + for k, n in list(nodes.items()): if holes[k] in (hl, hr): face_lines.append(Line([n.coords[0], n.coords[1]], Line.VERTICAL)) @@ -340,7 +365,8 @@ def elementary_volumes(holes_, starts_, ends_, hole_ids_, node_ids_, nodes_, end bug = 0 for i, li in enumerate(linework): if li.length <= 0: - print('zero length line', i, li.wkt) + # fix_print_with_import + print(('zero length line', i, li.wkt)) bug = True break found = False @@ -348,7 +374,8 @@ def elementary_volumes(holes_, starts_, ends_, hole_ids_, node_ids_, nodes_, end if i!=j and (not (lj.coords[0] != li.coords[0] or lj.coords[1] != li.coords[1]) \ or not (lj.coords[0] != li.coords[1] or lj.coords[1] != li.coords[0])): open("/tmp/dup_line_{}_face_{}.vtk".format(bug, face_idx), 'w').write(to_vtk(MultiLineString([LineString(linework_sav[j])]).wkb_hex)) - print('duplicate line', li.wkt, lj.wkt) + # fix_print_with_import + print(('duplicate line', li.wkt, lj.wkt)) bug += 1 if i!=j and li.coords[1] == lj.coords[0] or li.coords[1] == lj.coords[1]: found = True @@ -356,7 +383,8 @@ def elementary_volumes(holes_, starts_, ends_, hole_ids_, node_ids_, nodes_, end print(MultiLineString(linework).wkt) bug += 1 if bug: - print('open', MultiLineString(linework).wkt) + # fix_print_with_import + print(('open', MultiLineString(linework).wkt)) assert(False) @@ -383,17 +411,19 @@ def elementary_volumes(holes_, starts_, ends_, hole_ids_, node_ids_, nodes_, end term_tri.append(q) result += domain_tri + faces[(hl, hr)] += domain_tri top_lines = [l for l in face_lines if l.side==Line.TOP] bottom_lines = [l for l in face_lines if l.side==Line.BOTTOM] - end_lines = [tuple(nodes[n].coords) for n in ends.keys()] + end_lines = {tuple(nodes[n].coords): holes[n] for n in list(ends.keys())} if DEBUG: open('/tmp/top_lines_face_{}.vtk'.format(face_idx), 'w').write(to_vtk(MultiLineString([l.points for l in top_lines]).wkb_hex)) open('/tmp/bottom_lines_face_{}.vtk'.format(face_idx), 'w').write(to_vtk(MultiLineString([l.points for l in bottom_lines]).wkb_hex)) open('/tmp/offsets_bis.vtk', 'w').write( - to_vtk(MultiLineString([LineString([k, v]) for k, v in offsets.items()]).wkb_hex)) + to_vtk(MultiLineString([LineString([k, v]) for k, v in list(offsets.items())]).wkb_hex)) # create terminations + terms = [] edges = set() for t in term_tri: for s, e in zip(t.exterior.coords[:-1], t.exterior.coords[1:]): @@ -409,21 +439,24 @@ def elementary_volumes(holes_, starts_, ends_, hole_ids_, node_ids_, nodes_, end break if share: continue - termination.append(t) - termination.append(Polygon(offset_coords(offsets, t.exterior.coords[::-1]))) + terms.append(t) + faces[(hl, hr)] += [t] + terms.append(Polygon(offset_coords(offsets, t.exterior.coords[::-1]))) for s in zip(t.exterior.coords[:-1], t.exterior.coords[1:]): if s in edges: if (is_segment(s, top_lines) or is_segment(s, bottom_lines) or s in end_lines)\ and s[0] in offsets and s[1] in offsets: - termination.append(Polygon([offsets[s[0]], s[1], s[0]])) - termination.append(Polygon([offsets[s[0]], offsets[s[1]], s[1]])) + terms.append(Polygon([offsets[s[0]], s[1], s[0]])) + terms.append(Polygon([offsets[s[0]], offsets[s[1]], s[1]])) if (s[1], s[0]) in end_lines: - termination.append(Polygon([s[1], s[0], offsets[s[1]]])) - termination.append(Polygon([s[0], offsets[s[0]], offsets[s[1]]])) + terms.append(Polygon([s[1], s[0], offsets[s[1]]])) + terms.append(Polygon([s[0], offsets[s[0]], offsets[s[1]]])) + faces[tuple(sorted((end_lines[(s[1], s[0])], other_hole)))] += terms[-2:] + termination += terms if DEBUG: open("/tmp/faces.obj", 'w').write(to_obj(MultiPolygon(result).wkb_hex)) - open("/tmp/term.obj", 'w').write(to_obj(MultiPolygon(termination).wkb_hex)) + open("/tmp/termination.obj", 'w').write(to_obj(MultiPolygon(termination).wkb_hex)) if len(result): @@ -456,6 +489,7 @@ def elementary_volumes(holes_, starts_, ends_, hole_ids_, node_ids_, nodes_, end if DEBUG: open("/tmp/linework_%s.vtk"%(face), 'w').write(to_vtk(MultiLineString([LineString(e) for e in merged]).wkb_hex)) + face_triangles = [] for m in merged: if has_proper_2d_topology(m): node_map = {(round(x[0], PRECI), round(x[1], PRECI)): x for x in m} @@ -468,13 +502,21 @@ def elementary_volumes(holes_, starts_, ends_, hole_ids_, node_ids_, nodes_, end if face == 'bottom' else \ Polygon([node_map[tri[2]], node_map[tri[1]], node_map[tri[0]]]) result.append(q) + face_triangles.append(q) + if DEBUG: + open("/tmp/face_{}.obj".format(face), 'w').write(to_obj(MultiPolygon(face_triangles).wkb_hex)) + # adds isolated nodes terminations - for n, l in ends.items(): + for n, l in list(ends.items()): if len(l) == 2: node = nodes[n] A, B, C = array(node.coords[0]), array(l[0].coords[0]), array(l[1].coords[0]) - l = l if dot(cross(B-A, C-A), array((0.,0.,1.))) > 0 else list(reversed(l)) + k1, k2 = tuple(sorted((holes[n], end_holes[n][0]))), tuple(sorted((holes[n], end_holes[n][1]))) + l = l + if dot(cross(B-A, C-A), array((0.,0.,1.))) <= 0: + l = list(reversed(l)) + k1, k2 = k2, k1 termination += [ Polygon([node.coords[0], l[0].coords[0], l[1].coords[0]]), Polygon([l[1].coords[-1], l[0].coords[-1], node.coords[-1]]), @@ -485,9 +527,23 @@ def elementary_volumes(holes_, starts_, ends_, hole_ids_, node_ids_, nodes_, end Polygon([l[0].coords[0], l[0].coords[1], l[1].coords[0]]), Polygon([l[0].coords[1], l[1].coords[1], l[1].coords[0]]) ] + assert(len(end_holes[n])==2) + faces[k1] += [ + Polygon([node.coords[0], node.coords[1], l[0].coords[0]]), + Polygon([node.coords[1], l[0].coords[1], l[0].coords[0]]), + ] + faces[k2] += [ + Polygon([l[1].coords[0], node.coords[1], node.coords[0]]), + Polygon([l[1].coords[0], l[1].coords[1], node.coords[1]]), + ] result += termination + if DEBUG: + for hp, tri in faces.items(): + open("/tmp/face_{}_{}.obj".format(hp[0], hp[1]), 'w').write(to_obj(MultiPolygon([t for t in tri]).wkb_hex)) + + # decompose volume in connected components edges = {} graph = {i:set() for i in range(len(result))} @@ -510,13 +566,75 @@ def pop_connected(n, graph): connected = [] while len(graph): - n = next(iter(graph.keys())) + n = next(iter(list(graph.keys()))) connected.append(pop_connected(n, graph)) + i=0 for c in connected: - r = MultiPolygon([result[i] for i in c]) - geos.lgeos.GEOSSetSRID(r._geom, srid_) - yield r.wkb_hex + i+=1 + face1 = [] + face2 = [] + face3 = [] + triangles = [result[i] for i in c] + res = MultiPolygon(triangles) + + for f in faces[(sorted_holes[0], sorted_holes[1])]: + if f in triangles: + face1.append(f) + for f in faces[(sorted_holes[1], sorted_holes[2])]: + if f in triangles: + face2.append(f) + for f in faces[(sorted_holes[0], sorted_holes[2])]: + if f in triangles: + face3.append(f) + + if DEBUG: + open("/tmp/face1_tr_%d.obj"%(i), 'w').write(to_obj(face1.wkb_hex)) + open("/tmp/face2_tr_%d.obj"%(i), 'w').write(to_obj(face2.wkb_hex)) + open("/tmp/face3_tr_%d.obj"%(i), 'w').write(to_obj(face3.wkb_hex)) + open("/tmp/volume_tr.obj", 'w').write(to_obj(res.wkb_hex)) + # check volume is closed + edges = set() + for p in res: + for s, e in zip(p.exterior.coords[:-1], p.exterior.coords[1:]): + if (e, s) in edges: + edges.remove((e, s)) + else: + edges.add((s, e)) + if len(edges): + print("volume is not closed", edges) + open("/tmp/unconnected_edge.vtk", 'w').write(to_vtk(MultiLineString([LineString(e) for e in edges]).wkb_hex)) + + + # check volume is positive + volume = 0 + for p in res: + r = p.exterior.coords + v210 = r[2][0]*r[1][1]*r[0][2]; + v120 = r[1][0]*r[2][1]*r[0][2]; + v201 = r[2][0]*r[0][1]*r[1][2]; + v021 = r[0][0]*r[2][1]*r[1][2]; + v102 = r[1][0]*r[0][1]*r[2][2]; + v012 = r[0][0]*r[1][1]*r[2][2]; + volume += (1./6.)*(-v210 + v120 + v201 - v021 - v102 + v012) + if volume <= 0 : + print("volume is", volume) + + res = translate(res, translation[0], translation[1], translation[2]) + geos.lgeos.GEOSSetSRID(res._geom, srid_) + + face1 = translate(MultiPolygon(face1), translation[0], translation[1], translation[2]) + geos.lgeos.GEOSSetSRID(face1._geom, srid_) + + face2 = translate(MultiPolygon(face2), translation[0], translation[1], translation[2]) + geos.lgeos.GEOSSetSRID(face2._geom, srid_) + + face3 = translate(MultiPolygon(face3), translation[0], translation[1], translation[2]) + geos.lgeos.GEOSSetSRID(face3._geom, srid_) + + empty_mp = "SRID={} ;MULTIPOLYGONZ EMPTY".format(srid_) + yield (res.wkb_hex if not res.is_empty else empty_mp, face1.wkb_hex if not face1.is_empty else empty_mp, + face2.wkb_hex if not face2.is_empty else empty_mp, face3.wkb_hex if not face3.is_empty else empty_mp) for f in debug_files: os.remove(f) diff --git a/elementary_volume/__main__.py b/elementary_volume/__main__.py index 86df4f5..654b5da 100644 --- a/elementary_volume/__main__.py +++ b/elementary_volume/__main__.py @@ -13,7 +13,11 @@ nodes_ = f.readline().rstrip().split() end_ids_ = f.readline().rstrip().split() end_geoms_ = f.readline().rstrip().split() + end_holes_ = f.readline().rstrip().split() idx = 0 - for v in elementary_volumes(holes_, starts_, ends_, hole_ids_, node_ids_, nodes_, end_ids_, end_geoms_): + for v, f1, f2, f3 in elementary_volumes(holes_, starts_, ends_, hole_ids_, node_ids_, nodes_, end_ids_, end_geoms_, end_holes_): open("/tmp/volume%d.obj"%(idx), 'w').write(to_obj(v)) + open("/tmp/face1_%d.obj"%(idx), 'w').write(to_obj(f1)) + open("/tmp/face2_%d.obj"%(idx), 'w').write(to_obj(f2)) + open("/tmp/face3_%d.obj"%(idx), 'w').write(to_obj(f3)) idx += 1 diff --git a/export_elementary_volume.py b/export_elementary_volume.py index 8d26407..3e8e872 100644 --- a/export_elementary_volume.py +++ b/export_elementary_volume.py @@ -1,8 +1,9 @@ import os -from PyQt4 import uic -from PyQt4.QtCore import Qt -from PyQt4.QtGui import QDialog, QFileDialog, QApplication, QCursor +from qgis.PyQt import uic +from qgis.PyQt.QtCore import Qt +from qgis.PyQt.QtWidgets import QDialog, QFileDialog, QApplication +from qgis.PyQt.QtGui import QCursor from qgis.core import QgsFeatureRequest @@ -46,36 +47,20 @@ def __export(self): QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) QApplication.processEvents() - for fid in fids: - request = QgsFeatureRequest(fid) - - ft = None - for feature in self.cell_layer.getFeatures(request): - ft = feature - - if not ft: - return - - cell = ft["id"] - outdir = self.mOutputDir.text() - - if self.mFormat.currentText() == "OBJ": - self.project.export_elementary_volume_obj( - self.graph, cell, outdir, True - ) - else: # DXF - self.project.export_elementary_volume_dxf( - self.graph, cell, outdir, True - ) - - if not closed_only: - if self.mFormat.currentText() == "OBJ": - self.project.export_elementary_volume_obj( - self.graph, cell, outdir, False - ) - else: # DXF - self.project.export_elementary_volume_dxf( - self.graph, cell, outdir, False - ) + cell_ids = [feature["id"] + for fid in fids + for feature in self.cell_layer.getFeatures(QgsFeatureRequest(fid)) + ] + + outdir = self.mOutputDir.text() + + if self.mFormat.currentText() == "OBJ": + self.project.export_elementary_volume_obj( + self.graph, cell_ids, outdir, closed_only + ) + else: # DXF + self.project.export_elementary_volume_dxf( + self.graph, cell_ids, outdir, closed_only + ) QApplication.restoreOverrideCursor() diff --git a/import_test.py b/import_test.py deleted file mode 100644 index 184d4ff..0000000 --- a/import_test.py +++ /dev/null @@ -1,22 +0,0 @@ -# coding = utf-8 - -if __name__ == "__main__": - from albion.project import Project - import os - import sys - import time - - - project_name = "import_test" - if Project.exists(project_name): - Project.delete(project_name) - - project = Project.create(project_name, 32632) - start = time.time() - project.import_data(sys.argv[1]) - print "time for import", time.time() - start, 'sec' - #project = Project(project_name) - # - #project.triangulate() - #project.create_section_view_0_90(4) - diff --git a/log_strati.py b/log_strati.py index dc0d40f..63d2e86 100644 --- a/log_strati.py +++ b/log_strati.py @@ -1,10 +1,12 @@ # coding=utf-8 -from qgis.core import QgsDataSourceURI +from builtins import str +from builtins import range -from PyQt4 import uic -from PyQt4.QtGui import QGraphicsScene, QImage, QPixmap, QMainWindow, QBrush, QColor, QWheelEvent, QPen -from PyQt4.QtCore import Qt, QObject +from qgis.PyQt import uic +from qgis.PyQt.QtWidgets import QGraphicsScene, QMainWindow +from qgis.PyQt.QtGui import QImage, QPixmap, QBrush, QColor, QPen +from qgis.PyQt.QtCore import Qt, QObject from shapely import wkb import os @@ -227,8 +229,8 @@ def __init__(self, conn_info, parent=None): if __name__=='__main__': import sys - from PyQt4.QtCore import QSettings - from PyQt4.QtGui import QApplication + from qgis.PyQt.QtCore import QSettings + from qgis.PyQt.QtWidgets import QApplication QApplication.setOrganizationName("QGIS") QApplication.setOrganizationDomain("qgis.org") diff --git a/metadata.txt b/metadata.txt index e4ac93f..ccf4c9c 100644 --- a/metadata.txt +++ b/metadata.txt @@ -1,8 +1,8 @@ [general] name=Albion -qgisMinimumVersion=2.14 +qgisMinimumVersion=3.2 description=Build 3D geological model from wells information -version=3.0.0 +version=2.2.0 author=Oslandia email=infos@oslandia.com experimental=False diff --git a/mineralization.py b/mineralization.py index 8c7854d..fac5786 100644 --- a/mineralization.py +++ b/mineralization.py @@ -1,7 +1,7 @@ # coding = utf-8 import qgis.core -from PyQt4.QtGui import QDialog -from PyQt4 import uic +from qgis.PyQt.QtWidgets import QDialog +from qgis.PyQt import uic import os class MineralizationDialog(QDialog): @@ -15,7 +15,7 @@ def accept(self): self.close() if __name__=="__main__": - from PyQt4.QtGui import QApplication + from qgis.PyQt.QtWidgets import QApplication import sys from albion.project import Project diff --git a/package.py b/package.py index 0650b67..725d114 100644 --- a/package.py +++ b/package.py @@ -19,7 +19,9 @@ -t launch the tests before installing/uninstalling """ +from __future__ import print_function +from builtins import str import os import zipfile import re @@ -30,7 +32,7 @@ # @todo make that work on windows -qgis_plugin_dir = os.path.join(os.path.expanduser("~"), ".qgis2", "python", "plugins") +qgis_plugin_dir = os.path.join(os.path.expanduser("~"), ".local", "share", "QGIS", "QGIS3", "profiles", "default", "python", "plugins") zipname = "albion" zipext = ".zip" diff --git a/plugin.py b/plugin.py index a0458f2..8ad4475 100644 --- a/plugin.py +++ b/plugin.py @@ -1,25 +1,12 @@ # coding: utf-8 +from builtins import str from qgis.core import * from qgis.gui import * -from PyQt4.QtCore import QObject, Qt, QFileInfo, QUrl -from PyQt4.QtGui import ( - QComboBox, - QShortcut, - QKeySequence, - QToolBar, - QIcon, - QMenu, - QFileDialog, - QInputDialog, - QLineEdit, - QMessageBox, - QProgressBar, - QApplication, - QDockWidget, - QDesktopServices, -) +from qgis.PyQt.QtCore import QObject, Qt, QUrl +from qgis.PyQt.QtWidgets import QComboBox, QShortcut, QToolBar, QMenu, QFileDialog, QInputDialog, QLineEdit, QMessageBox, QProgressBar, QApplication, QDockWidget +from qgis.PyQt.QtGui import QKeySequence, QIcon, QDesktopServices import psycopg2 import os @@ -29,10 +16,9 @@ from .project import ProgressBar, Project, find_in_dir from .mineralization import MineralizationDialog -from .axis_layer import AxisLayer, AxisLayerType from .viewer_3d.viewer_3d import Viewer3d from .viewer_3d.viewer_controls import ViewerControls -from .log_strati import BoreHoleWindow +#from .log_strati import BoreHoleWindow from .export_elementary_volume import ExportElementaryVolume @@ -42,13 +28,6 @@ import atexit -AXIS_LAYER_TYPE = AxisLayerType() -QgsPluginLayerRegistry.instance().addPluginLayerType(AXIS_LAYER_TYPE) -atexit.register( - QgsPluginLayerRegistry.instance().removePluginLayerType, AxisLayer.LAYER_TYPE -) - - def resource(name): """Return name with prepended `res` directory """ @@ -71,7 +50,6 @@ def __init__(self, iface): self.__current_graph = QComboBox() self.__current_graph.setMinimumWidth(150) self.__toolbar = None - self.__axis_layer = None self.__menu = None self.__log_strati = None @@ -79,9 +57,10 @@ def initGui(self): for keyseq, slot in ( (Qt.CTRL + Qt.ALT + Qt.Key_K, self.__create_group), - (Qt.CTRL + Qt.ALT + Qt.Key_S, self.__select_next_group), +# (Qt.CTRL + Qt.ALT + Qt.Key_S, self.__select_next_group), (Qt.CTRL + Qt.ALT + Qt.Key_N, self.__next_section), (Qt.CTRL + Qt.ALT + Qt.Key_B, self.__previous_section), + (Qt.CTRL + Qt.ALT + Qt.Key_J, self.__add_section_from_selection), ): short = QShortcut(QKeySequence(keyseq), self.__iface.mainWindow()) @@ -96,28 +75,38 @@ def initGui(self): self.__toolbar = QToolBar("Albion") self.__iface.addToolBar(self.__toolbar) - self.__toolbar.addAction( - icon("log_strati.svg"), "stratigraphic log" - ).triggered.connect(self.__log_strati_clicked) + #self.__toolbar.addAction( + # icon("log_strati.svg"), "stratigraphic log" + #).triggered.connect(self.__log_strati_clicked) self.__toolbar.addWidget(self.__current_graph) - self.__current_graph.currentIndexChanged[unicode].connect( + self.__current_graph.currentIndexChanged[str].connect( self.__current_graph_changed ) self.__toolbar.addWidget(self.__current_section) - self.__current_section.currentIndexChanged[unicode].connect( + self.__current_section.currentIndexChanged[str].connect( self.__current_section_changed ) self.__toolbar.addAction( - icon("previous_line.svg"), "previous section (Ctrl+Alt+b)" + icon("previous_line_big.svg"), "previous section (Ctrl+Alt+b)" ).triggered.connect(self.__previous_section) + + self.__toolbar.addAction( + icon("previous_line.svg"), "previous sub section" + ).triggered.connect(self.__previous_subsection) + + self.__toolbar.addAction( + icon("next_line.svg"), "next sub section" + ).triggered.connect(self.__next_subsection) + self.__toolbar.addAction( - icon("next_line.svg"), "next section (Ctrl+Alt+n)" + icon("next_line_big.svg"), "next section (Ctrl+Alt+n)" ).triggered.connect(self.__next_section) + self.__toolbar.addAction( icon("line_from_selected.svg"), "create temporary section" ).triggered.connect(self.__section_from_selection) @@ -156,107 +145,122 @@ def __add_menu_entry(self, name, callback, enabled=True, help_str=""): return act def __create_menu_entries(self): + self.__menu.clear() + self.__add_menu_entry("New &Project", self.__new_project) + self.__add_menu_entry("Import Project", self.__import_project) + + self.__add_menu_entry("Export Project", self.__export_project, self.project is not None) + self.__add_menu_entry("Upgrade Project", self.__upgrade_project) self.__menu.addSeparator() self.__add_menu_entry( - "&Import Data", + "&Import directory", self.__import_data, self.project is not None, - "Import data from directory. The data files are in ", + "Import data from directory" ) - self.__menu.addSeparator() - self.__add_menu_entry( - "Create cells", - self.__create_cells, - self.project is not None and self.project.has_collar, - "Create Delaunay triangulation of collar layer.", + "&Import holes", + None, #self.__import_holes, + self.project is not None and False, + "Import hole data from directory" ) self.__add_menu_entry( - "Refresh all edges", - self.__refresh_all_edge, - self.project is not None and self.project.has_cell, - "Refresh materialized view of all cell edges used by graph possible edges.", + "Export holes", + self.__export_holes, + self.project is not None and self.project.has_hole, + "Export hole trace in .vtk or .dxf format", ) self.__add_menu_entry( - "Refresh diagraphy", - self.__refresh_diagraphy, + "Import layer", + self.__import_layer, self.project is not None, - "Refresh materialized view of radiometry and resistivity.", + "Import data from selected layer." + ) + + self.__add_menu_entry( + "Export layer", + self.__export_layer, + self.project is not None + ) + + self.__add_menu_entry( + "Compute &Mineralization", + self.__compute_mineralization, + self.project is not None and self.project.has_radiometry, + "", ) + self.__menu.addSeparator() self.__menu.addSeparator() - # self.__add_menu_entry(u'Create section views 0° and 90°', self.__create_section_view_0_90, - # False and self.project is not None and self.project.has_hole, - # "Create section views (i.e. cut directions) to work West-East and South-North for this project") - # self.__add_menu_entry(u'Create section views -45° and 45°', None, - # False and self.project is not None and self.project.has_hole, - # "Create section views (i.e. cut directions) to work SE-NW and SW-NE for this project") - # - # self.__menu.addSeparator() + self.__add_menu_entry( + "Create cells", + self.__create_cells, + self.project is not None and self.project.has_hole, + "Create Delaunay triangulation of collar layer.", + ) self.__add_menu_entry( - "Create sections", + "Create subsections", self.__create_sections, self.project is not None and self.project.has_group_cell, "Once cell groups have been defined, create section lines.", ) - self.__menu.addSeparator() - self.__add_menu_entry( - "Compute &Mineralization", - self.__compute_mineralization, - self.project is not None and self.project.has_radiometry, - "", + "Refresh selected layers sections", + self.__refresh_selected_layers_sections, + self.project is not None, + "" ) + self.__menu.addSeparator() + self.__add_menu_entry( "New &Graph", self.__new_graph, self.project is not None, - "Create a new grap.h", + "Create a new graph", ) + self.__add_menu_entry( - "Delete Graph", self.__delete_graph, self.project is not None + "Delete Graph", self.__delete_graph, self.project is not None and self.project.has_graph ) - self.__menu.addSeparator() + self.__add_menu_entry( - "Create volumes", - self.__create_volumes, - self.project is not None and bool(self.__current_graph.currentText()), - "Create volumes associated with current graph.", + "Add selection to graph nodes", self.__add_selection_to_graph_node, self.project is not None and self.project.has_graph + ) - self.__menu.addSeparator() + + self.__add_menu_entry( + "Accept graph possible edges", self.__accept_possible_edge, self.project is not None and self.project.has_graph + ) + self.__add_menu_entry( "Create terminations", self.__create_terminations, self.project is not None and bool(self.__current_graph.currentText()), "Create terminations associated with current graph.", ) - self.__menu.addSeparator() - self.__add_menu_entry("Toggle axis", self.__toggle_axis) self.__menu.addSeparator() - - self.__add_menu_entry( - "Export Project", self.__export_project, self.project is not None - ) + self.__add_menu_entry( - "Export Sections", - None, - self.project is not None and self.project.has_section, - "Export triangulated section in .obj or .dxf format", + "Create volumes", + self.__create_volumes, + self.project is not None and bool(self.__current_graph.currentText()), + "Create volumes associated with current graph.", ) + self.__add_menu_entry( "Export Volume", self.__export_volume, @@ -271,6 +275,13 @@ def __create_menu_entries(self): "Export an elementary volume of current graph in .obj or .dxf format", ) + self.__add_menu_entry( + "Export Sections", + self.__export_sections, + self.project is not None and bool(self.__current_graph.currentText()) and self.project.has_section and self.project.has_volume, + "Export triangulated section in .obj or .dxf format", + ) + self.__menu.addSeparator() self.__menu.addAction("Help").triggered.connect(self.open_help) @@ -289,45 +300,49 @@ def __getattr__(self, name): else: raise AttributeError(name) - def __refresh_all_edge(self): - if self.project is None: - return - self.project.refresh_all_edge() - - def __refresh_diagraphy(self): - if self.project: - self.project.refresh_radiometry() - self.project.refresh_resistivity() - def __create_terminations(self): - if self.project is None: - return self.project.create_terminations(self.__current_graph.currentText()) self.__viewer3d.widget().refresh_data() self.__refresh_layers("section") def __create_volumes(self): - if self.project is None: - return self.project.create_volumes(self.__current_graph.currentText()) self.__viewer3d.widget().refresh_data() def __next_section(self): - if self.project is None: - return self.project.next_section(self.__current_section.currentText()) self.__refresh_layers("section") self.__viewer3d.widget().scene.update("section") + self.__viewer3d.widget().scene.update("volume_section") self.__viewer3d.widget().update() def __previous_section(self): - if self.project is None: - return self.project.previous_section(self.__current_section.currentText()) self.__refresh_layers("section") self.__viewer3d.widget().scene.update("section") + self.__viewer3d.widget().scene.update("volume_section") self.__viewer3d.widget().update() + def __next_subsection(self): + self.project.next_subsection(self.__current_section.currentText()) + print("refresh") + self.__refresh_layers("section") + print("section") + self.__viewer3d.widget().scene.update("section") + print("volume section") + self.__viewer3d.widget().scene.update("volume_section") + print("3D update") + self.__viewer3d.widget().update() + print("done done") + + def __previous_subsection(self): + self.project.previous_subsection(self.__current_section.currentText()) + self.__refresh_layers("section") + self.__viewer3d.widget().scene.update("section") + self.__viewer3d.widget().scene.update("volume_section") + self.__viewer3d.widget().update() + + def __refresh_layers(self, name=None, updateExtent=False): for layer in self.__iface.mapCanvas().layers(): if name is None or layer.name().find(name) != -1: @@ -343,26 +358,24 @@ def __layer(self, name): return lay def __current_section_changed(self, section_id): - layers = QgsMapLayerRegistry.instance().mapLayersByName(u"group_cell") + layers = QgsProject.instance().mapLayersByName(u"group_cell") if len(layers): layers[0].setSubsetString("section_id='{}'".format(section_id)) self.__refresh_layers("section") - def __select_next_group(self): - if ( - self.project - and self.__iface.activeLayer() - and self.__iface.activeLayer().name() == u"cell" - ): - self.__iface.activeLayer().removeSelection() - self.__iface.activeLayer().selectByExpression( - "id in ({})".format(",".join(project.next_group_ids())) - ) - +# def __select_next_group(self): +# if ( +# self.__iface.activeLayer() +# and self.__iface.activeLayer().name() == u"cell" +# ): +# self.__iface.activeLayer().removeSelection() +# self.__iface.activeLayer().selectByExpression( +# "id in ({})".format(",".join(project.next_group_ids())) +# ) +# def __create_group(self): if ( - self.project - and self.__iface.activeLayer() + self.__iface.activeLayer() and self.__iface.activeLayer().name() == u"cell" ): if self.__iface.activeLayer().selectedFeatureCount(): @@ -382,7 +395,7 @@ def __qgis__project__loaded(self): self.__current_section.addItems(self.project.sections()) self.__current_graph.addItems(self.project.graphs()) - layers = QgsMapLayerRegistry.instance().mapLayersByName("section.anchor") + layers = QgsProject.instance().mapLayersByName("section.anchor") if len(layers): layers[0].editingStopped.connect(self.__update_section_list) @@ -390,10 +403,11 @@ def __qgis__project__loaded(self): # We make sure that corresponding extents are valid when the project # is loaded - cell = QgsMapLayerRegistry.instance().mapLayersByName("cell")[0] - cell.updateExtents() + cell = QgsProject.instance().mapLayersByName("cell") + if len(cell): + cell[0].updateExtents() - section_geom = QgsMapLayerRegistry.instance().mapLayersByName("section.geom") + section_geom = QgsProject.instance().mapLayersByName("section.geom") if section_geom: section_geom[0].updateExtents() @@ -411,12 +425,16 @@ def __upgrade_project(self): ) if not ok: return - Project(project_name).update() + project = Project(project_name) + project.update() + QgsProject.instance().writeEntry("albion", "project_name", project.name) + QgsProject.instance().writeEntry("albion", "srid", project.srid) + self.__qgis__project__loaded() + def __new_project(self): - # @todo open dialog to configure project name and srid - fil = QFileDialog.getSaveFileName( + fil, __ = QFileDialog.getSaveFileName( None, u"New project name (no space, plain ascii)", QgsProject.instance().readEntry("albion", "last_dir", "")[0], @@ -426,9 +444,7 @@ def __new_project(self): return fil = fil if len(fil) > 4 and fil[-4:] == ".qgs" else fil + ".qgs" fil = fil.replace(" ", "_") - try: - fil.decode("ascii") - except UnicodeDecodeError: + if len(fil) != len(fil.encode()): self.__iface.messageBar().pushError( "Albion:", "project name may only contain asci character (no accent)" ) @@ -453,23 +469,30 @@ def __new_project(self): != QMessageBox( QMessageBox.Information, "Delete existing DB", - "Database {} exits, to you want to delete it ?".format( + "Database {} exits, do you want to delete it ?".format( project_name ), QMessageBox.Yes | QMessageBox.No, ).exec_() ): - return - Project.delete(project_name) + self.__iface.messageBar().pushInfo("Albion:", "keeping existing database...") + else: + Project.delete(project_name) + self.__iface.messageBar().pushInfo("Albion:", "creating project...") + Project.create(project_name, srid) + else: + self.__iface.messageBar().pushInfo("Albion:", "creating project...") + Project.create(project_name, srid) - self.__iface.messageBar().pushInfo("Albion:", "creating project...") - Project.create(project_name, srid) + if os.path.exists(fil): + os.remove(fil) # load template open(fil, "w").write( open(resource("template_project.qgs")) .read() .replace("template_project", project_name) + .replace("32632", str(srid)) ) self.__iface.newProject() QgsProject.instance().setFileName(fil) @@ -480,14 +503,12 @@ def __new_project(self): self.__qgis__project__loaded() def __import_data(self): - if self.project is None: - return - if not QgsProject.instance().readEntry("albion", "conn_info", "")[0]: - return + assert(self.project) dir_ = QFileDialog.getExistingDirectory( None, u"Data directory", QgsProject.instance().readEntry("albion", "last_dir", "")[0], + QFileDialog.ShowDirsOnly | QFileDialog.DontUseNativeDialog ) if not dir_: return @@ -499,24 +520,23 @@ def __import_data(self): progress = QProgressBar() progress.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) progressMessageBar.layout().addWidget(progress) - self.__iface.messageBar().pushWidget( - progressMessageBar, self.__iface.messageBar().INFO - ) + self.__iface.messageBar().pushWidget(progressMessageBar) self.project.import_data(dir_, ProgressBar(progress)) - self.project.triangulate() + #self.project.triangulate() self.project.create_section_view_0_90(4) self.__iface.messageBar().clearWidgets() - collar = QgsMapLayerRegistry.instance().mapLayersByName("collar")[0] - collar.reload() - collar.updateExtents() - self.__iface.setActiveLayer(collar) - QApplication.instance().processEvents() - while self.__iface.mapCanvas().isDrawing(): + collar = QgsProject.instance().mapLayersByName("collar") + if len(collar): + collar[0].reload() + collar[0].updateExtents() + self.__iface.setActiveLayer(collar[0]) QApplication.instance().processEvents() - self.__iface.zoomToActiveLayer() + while self.__iface.mapCanvas().isDrawing(): + QApplication.instance().processEvents() + self.__iface.zoomToActiveLayer() self.__iface.actionSaveProject().trigger() @@ -524,10 +544,54 @@ def __import_data(self): self.__current_section.clear() self.__current_section.addItems(self.project.sections()) - def __new_graph(self): + def __import_layer(self): + assert(self.project) + if self.__iface.activeLayer(): + from_idx = None + to_idx = None + hole_id_idx= None + other_idx = [] + definitions = [] + fields = [] + for idx, f in enumerate(self.__iface.activeLayer().fields()): + if f.name().lower() == 'from' or f.name().lower() == 'from_': + from_idx = idx + elif f.name().lower() == 'to' or f.name().lower() == 'to_': + to_idx = idx + elif f.name().lower() == 'hole_id' or f.name().lower() == 'holeid': + hole_id_idx = idx + else: + other_idx.append(idx) + name = f.name().lower().replace(' ', '_') + fields.append(name) + type_ = 'varchar' + if f.typeName() == 'double': + type_ = 'double precision' + elif f.typeName() == 'integer': + type_ = 'integer' + definitions.append(name + ' ' + type_) + + table = { + 'NAME': self.__iface.activeLayer().name().lower().replace(' ', '_'), + 'FIELDS_DEFINITION': ', '.join(definitions), + 'FIELDS': ', '.join(fields), + 'SRID': self.project.srid + } + + if from_idx is None or to_idx is None or hole_id_idx is None: + self.__iface.messageBar().pushCritical( + "Albion", "imported layer must have 'to', 'from' and 'hole_id' fields") + return - if self.project is None: - return + values = [] + for f in self.__iface.activeLayer().getFeatures(): + values.append((f[hole_id_idx], f[from_idx], f[to_idx]) + + tuple((f[i] for i in other_idx))) + self.project.add_table(table, values) + + + + def __new_graph(self): graph, ok = QInputDialog.getText( self.__iface.mainWindow(), @@ -556,8 +620,6 @@ def __new_graph(self): self.__current_graph.setCurrentIndex(self.__current_graph.findText(graph)) def __delete_graph(self): - if self.project is None: - return graph, ok = QInputDialog.getText( self.__iface.mainWindow(), @@ -571,38 +633,78 @@ def __delete_graph(self): return self.__current_graph.removeItem(self.__current_graph.findText(graph)) + self.project.delete_graph(graph) + + def __add_selection_to_graph_node(self): + assert(self.project) + #TODO ADD DIALOG TO REMIND USER THE CURRENT GRAPH + if ( + self.__iface.activeLayer() + and self.__iface.activeLayer().selectedFeatures() + ): + selection = self.__iface.activeLayer().selectedFeatures() + graph = self.__current_graph.currentText() + if ( + QMessageBox.Yes + != QMessageBox( + QMessageBox.Information, + "Adding selected edges", + "Do you want to add {} selected edges to {} ?".format( + len(selection), + graph + ), + QMessageBox.Yes | QMessageBox.No, + ).exec_() + ): + return + + self.project.add_to_graph_node(graph, selection) - def __toggle_axis(self): - if self.__axis_layer: - pass - QgsMapLayerRegistry.instance().removeMapLayer(self.__axis_layer.id()) - self.__axis_layer = None - else: - self.__axis_layer = AxisLayer( - self.__iface.mapCanvas().mapSettings().destinationCrs() - ) - QgsMapLayerRegistry.instance().addMapLayer(self.__axis_layer) self.__refresh_layers() + def __accept_possible_edge(self): + assert(self.project) + self.project.accept_possible_edge(self.__current_graph.currentText()) + def __create_cells(self): - if self.project is None: - return + assert(self.project) + + if self.project.has_cell: + if ( + QMessageBox.Yes + != QMessageBox( + QMessageBox.Information, + "Creating cells", + "Do you want to replace project cells (your graphs will become invalid) ?", + QMessageBox.Yes | QMessageBox.No, + ).exec_() + ): + return + self.project.triangulate() - self.__refresh_layers("cells") + self.__refresh_layers() def __create_sections(self): - if self.project is None: - return + assert(self.project) self.project.create_sections() + def __refresh_selected_layers_sections(self): + assert(self.project) + for l in self.__iface.layerTreeView().selectedLayers(): + uri = QgsDataSourceUri(l.dataProvider().dataSourceUri()) + table = uri.table() + if table.endswith('_section'): + table = table[:-8] + self.project.refresh_section_geom(table) + self.__refresh_layers(table+'_section') + def __compute_mineralization(self): MineralizationDialog(self.project).exec_() def __export_volume(self): - if self.project is None: - return + assert(self.project) - fil = QFileDialog.getSaveFileName( + fil, __ = QFileDialog.getSaveFileName( None, u"Export volume for current graph", QgsProject.instance().readEntry("albion", "last_dir", "")[0], @@ -623,11 +725,13 @@ def __export_volume(self): ) def __export_elementary_volume(self): - if self.project is None: - return + assert(self.project) layer = self.__layer("cell") if not layer: + self.__iface.messageBar().pushWarning( + "Albion", "cell layer must be selected" + ) return graph = self.__current_graph.currentText() @@ -635,8 +739,87 @@ def __export_elementary_volume(self): export_widget.show() export_widget.exec_() + def __export_sections(self): + assert(self.project) + + fil, __ = QFileDialog.getSaveFileName( + None, + u"Export named sections for current graph", + QgsProject.instance().readEntry("albion", "last_dir", "")[0], + "File formats (*.dxf *.obj)", + ) + if not fil: + return + + QgsProject.instance().writeEntry("albion", "last_dir", os.path.dirname(fil)) + + if fil[-4:] == ".obj": + self.project.export_sections_obj(self.__current_graph.currentText(), fil) + elif fil[-4:] == ".dxf": + self.project.export_sections_dxf(self.__current_graph.currentText(), fil) + else: + self.__iface.messageBar().pushWarning( + "Albion", "unsupported extension for section export" + ) + + + def __export_holes(self): + assert(self.project) + + fil, __ = QFileDialog.getSaveFileName( + None, + u"Export holes", + QgsProject.instance().readEntry("albion", "last_dir", "")[0], + "File formats (*.dxf *.vtk)", + ) + if not fil: + return + + QgsProject.instance().writeEntry("albion", "last_dir", os.path.dirname(fil)) + + if fil[-4:] == ".vtk": + self.project.export_holes_vtk(fil) + elif fil[-4:] == ".dxf": + self.project.export_holes_dxf(fil) + else: + self.__iface.messageBar().pushWarning("Albion", "unsupported extension for hole export") + + def __export_layer(self): + assert(self.project) + + table = None + for l in self.__iface.layerTreeView().selectedLayers(): + uri = QgsDataSourceUri(l.dataProvider().dataSourceUri()) + table = uri.table() + if table.endswith('_section'): + table = table[:-8] + break + + if table is None: + self.__iface.messageBar().pushWarning("Albion", "you must select a layer") + return + + fil, __ = QFileDialog.getSaveFileName( + None, + u"Export layer", + QgsProject.instance().readEntry("albion", "last_dir", "")[0], + "File formats (*.dxf *.vtk)", + ) + if not fil: + return + + QgsProject.instance().writeEntry("albion", "last_dir", os.path.dirname(fil)) + + + if fil.endswith('.vtk'): + self.project.export_layer_vtk(table, fil) + elif fil.endswith('.dxf'): + self.project.export_layer_dxf(table, fil) + else: + self.__iface.messageBar().pushWarning("Albion", "unsupported extension for hole export") + def __import_project(self): - fil = QFileDialog.getOpenFileName( + fil, __ = QFileDialog.getOpenFileName( None, u"Import project from file", QgsProject.instance().readEntry("albion", "last_dir", "")[0], @@ -683,13 +866,13 @@ def __import_project(self): project = Project.import_(dbname, dump) - QgsProject.instance().read(QFileInfo(prj)) + QgsProject.instance().read(prj) def __export_project(self): if self.project is None: return - fil = QFileDialog.getSaveFileName( + fil, __ = QFileDialog.getSaveFileName( None, u"Export project", QgsProject.instance().readEntry("albion", "last_dir", "")[0], @@ -712,45 +895,37 @@ def __export_project(self): os.path.split(QgsProject.instance().fileName())[1], ) - def __create_section_view_0_90(self): - if self.project is None: - return - self.project.create_section_view_0_90() - - def __log_strati_clicked(self): - # @todo switch behavior when in section view -> ortho - self.__click_tool = QgsMapToolEmitPoint(self.__iface.mapCanvas()) - self.__iface.mapCanvas().setMapTool(self.__click_tool) - self.__click_tool.canvasClicked.connect(self.__map_log_clicked) - - def __map_log_clicked(self, point, button): - self.__click_tool.setParent(None) - self.__click_tool = None - - if self.project is None: - self.__log_strati and self.__log_strati.setParent(None) - self.__log_strati = None - return - - if self.__log_strati is None: - self.__log_strati = QDockWidget("Stratigraphic Log") - self.__log_strati.setWidget(BoreHoleWindow(self.project)) - self.__iface.addDockWidget(Qt.LeftDockWidgetArea, self.__log_strati) - self.__iface.mainWindow().tabifyDockWidget( - self.__iface.mainWindow().findChild(QDockWidget, "Layers"), - self.__log_strati, - ) - - res = self.project.closest_hole_id(point.x(), point.y()) - if res: - self.__log_strati.widget().scene.set_current_id(res) - self.__log_strati.show() - self.__log_strati.raise_() - - def __section_from_selection(self): - if self.project is None: - return - + #def __log_strati_clicked(self): + # # @todo switch behavior when in section view -> ortho + # self.__click_tool = QgsMapToolEmitPoint(self.__iface.mapCanvas()) + # self.__iface.mapCanvas().setMapTool(self.__click_tool) + # self.__click_tool.canvasClicked.connect(self.__map_log_clicked) + + #def __map_log_clicked(self, point, button): + # self.__click_tool.setParent(None) + # self.__click_tool = None + + # if self.project is None: + # self.__log_strati and self.__log_strati.setParent(None) + # self.__log_strati = None + # return + + # if self.__log_strati is None: + # self.__log_strati = QDockWidget("Stratigraphic Log") + # self.__log_strati.setWidget(BoreHoleWindow(self.project)) + # self.__iface.addDockWidget(Qt.LeftDockWidgetArea, self.__log_strati) + # self.__iface.mainWindow().tabifyDockWidget( + # self.__iface.mainWindow().findChild(QDockWidget, "Layers"), + # self.__log_strati, + # ) + + # res = self.project.closest_hole_id(point.x(), point.y()) + # if res: + # self.__log_strati.widget().scene.set_current_id(res) + # self.__log_strati.show() + # self.__log_strati.raise_() + + def __line_from_selection(self): if ( self.__iface.activeLayer() and self.__iface.activeLayer().name() == u"collar" @@ -783,9 +958,26 @@ def align(l): align(numpy.array([f.geometry().asPoint() for f in selection])) ) collar.removeSelection() + return line + else: + return None + + def __add_section_from_selection(self): + assert(self.project) + line = self.__line_from_selection() + if line: + self.project.add_named_section(self.__current_section.currentText(), line) + self.__refresh_layers("named_section") + + + def __section_from_selection(self): + assert(self.project) + line = self.__line_from_selection() + if line: self.project.set_section_geom(self.__current_section.currentText(), line) self.__refresh_layers("section") self.__viewer3d.widget().scene.update("section") + self.__viewer3d.widget().scene.update("volume_section") self.__viewer3d.widget().update() def open_help(self): diff --git a/project.py b/project.py index fefc065..6d390ac 100644 --- a/project.py +++ b/project.py @@ -1,11 +1,14 @@ # coding = utf-8 +from builtins import str +from builtins import object from pglite import cluster_params import psycopg2 import os import sys import atexit import binascii +import string from shapely import wkb from dxfwrite import DXFEngine as dxf @@ -23,16 +26,67 @@ from qgis.core import QgsMessageLog +import time +from psycopg2.extras import LoggingConnection, LoggingCursor +import logging + +logging.basicConfig(level=logging.DEBUG) +logger = logging.getLogger(__name__) +# MyLoggingCursor simply sets self.timestamp at start of each query +class MyLoggingCursor(LoggingCursor): + def execute(self, query, vars=None): + self.timestamp = time.time() + return super(MyLoggingCursor, self).execute(query, vars) + + def callproc(self, procname, vars=None): + self.timestamp = time.time() + return super(MyLoggingCursor, self).callproc(procname, vars) + +# MyLogging Connection: +# a) calls MyLoggingCursor rather than the default +# b) adds resulting execution (+ transport) time via filter() +class MyLoggingConnection(LoggingConnection): + def filter(self, msg, curs): + return "{} {} ms".format(msg, int((time.time() - curs.timestamp) * 1000)) + + def cursor(self, *args, **kwargs): + kwargs.setdefault('cursor_factory', MyLoggingCursor) + return LoggingConnection.cursor(self, *args, **kwargs) + + if not check_cluster(): init_cluster() start_cluster() -# atexit.register(stop_cluster) + +#atexit.register(stop_cluster) +TABLES = [ + {'NAME': 'radiometry', + 'FIELDS_DEFINITION': 'gamma real', + }, + {'NAME': 'resistivity', + 'FIELDS_DEFINITION': 'rho real', + }, + {'NAME': 'formation', + 'FIELDS_DEFINITION': 'code integer, comments varchar', + }, + {'NAME': 'lithology', + 'FIELDS_DEFINITION': 'code integer, comments varchar', + }, + {'NAME': 'facies', + 'FIELDS_DEFINITION': 'code integer, comments varchar', + }, + {'NAME': 'chemical', + 'FIELDS_DEFINITION': 'num_sample varchar, element varchar, thickness real, gt real, grade real, equi real, comments varchar', + }, + {'NAME': 'mineralization', + 'FIELDS_DEFINITION': 'level_ real, oc real, accu real, grade real, comments varchar', + }] def find_in_dir(dir_, name): for filename in os.listdir(dir_): if filename.find(name) != -1: - return os.path.join(dir_, filename) + return os.path.abspath(os.path.join(dir_, filename)) return "" @@ -69,14 +123,16 @@ def __init__(self, project_name): self.__conn_info = "dbname={} {}".format(project_name, cluster_params()) def connect(self): - return psycopg2.connect(self.__conn_info) + con = psycopg2.connect(self.__conn_info)#, connection_factory=MyLoggingConnection) + #con.initialize(logger) + return con def vacuum(self): with self.connect() as con: - old_isolation_level = con.isolation_level con.set_isolation_level(0) cur = con.cursor() cur.execute("vacuum analyze") + con.commit() @staticmethod def exists(project_name): @@ -155,15 +211,79 @@ def create(project_name, srid): ) ) con.commit() + + for table in TABLES: + table['SRID'] = srid + project.add_table(table) + return project + + def add_table(self, table, values=None, view_only=False): + """ + table: a dict with keys + NAME: the name of the table to create + FIELDS_DEFINITION: the sql definition (name type) of the "additional" fields (i.e. excludes hole_id, from_ and to_) + SRID: the project's SRID + values: list of tuples (hole_id, from_, to_, ...) + """ + + fields = [f.split()[0].strip() for f in table['FIELDS_DEFINITION'].split(',')] + table['FIELDS'] = ', '.join(fields) + table['T_FIELDS'] = ', '.join(['t.{}'.format(f.replace(' ', '')) for f in fields]) + table['FORMAT'] = ','.join([' %s' for v in fields]) + table['NEW_FIELDS'] = ','.join(['new.{}'.format(v) for v in fields]) + table['SET_FIELDS'] = ','.join(['{}=new.{}'.format(v,v) for v in fields]) + with self.connect() as con: + cur = con.cursor() + for file_ in (("albion_table.sql",) if view_only else ("_albion_table.sql", "albion_table.sql")): + for statement in ( + open(os.path.join(os.path.dirname(__file__), file_)) + .read() + .split("\n;\n")[:-1] + ): + cur.execute( + string.Template(statement).substitute(table) + ) + if values is not None: + cur.executemany(""" + insert into albion.{NAME}(hole_id, from_, to_, {FIELDS}) + values (%s, %s, %s, {FORMAT}) + """.format(**table), values) + cur.execute(""" + refresh materialized view albion.{NAME}_section_geom_cache + """.format(**table)) + con.commit() + self.vacuum() + + def update(self): "reload schema albion without changing data" + with self.connect() as con: cur = con.cursor() cur.execute("select srid from albion.metadata") srid, = cur.fetchone() cur.execute("drop schema if exists albion cascade") + + # test if version number is in metadata + cur.execute(""" + select column_name + from information_schema.columns where table_name = 'metadata' + and column_name='version' + """); + if cur.fetchone(): + # here goes future upgrades + cur.execute("select version from _albion.metadata") + else: + # old albion version, we upgrade the data + for statement in ( + open(os.path.join(os.path.dirname(__file__), "_albion_v1_to_v2.sql")) + .read() + .split("\n;\n")[:-1] + ): + cur.execute(statement.replace("$SRID", str(srid))) + include_elementary_volume = open( os.path.join( os.path.dirname(__file__), "elementary_volume", "__init__.py" @@ -179,23 +299,99 @@ def update(self): "$INCLUDE_ELEMENTARY_VOLUME", include_elementary_volume ) ) + con.commit() + cur.execute("select name, fields_definition from albion.layer") + tables = [{'NAME': r[0], 'FIELDS_DEFINITION': r[1]} for r in cur.fetchall()] + + for table in tables: + table['SRID'] = str(srid) + self.add_table(table, view_only=True) + + self.vacuum() + + def export_sections_obj(self, graph, filename): + + with self.connect() as con: + cur = con.cursor() + cur.execute( + """ + with hole_idx as ( + select s.id as section_id, h.id as hole_id + from _albion.named_section as s + join _albion.hole as h on s.geom && h.geom and st_intersects(s.geom, st_startpoint(h.geom)) + ) + select albion.to_obj(st_collectionhomogenize(st_collect(ef.geom))) + from albion.all_edge as e + join hole_idx as hs on hs.hole_id = e.start_ + join hole_idx as he on he.hole_id = e.end_ and he.section_id = hs.section_id + join albion.edge_face as ef on ef.start_ = e.start_ and ef.end_ = e.end_ and not st_isempty(ef.geom) + where ef.graph_id='{}' + """.format( + graph + ) + ) + open(filename, "w").write(cur.fetchone()[0]) + + def export_sections_dxf(self, graph, filename): + + with self.connect() as con: + cur = con.cursor() + cur.execute( + """ + with hole_idx as ( + select s.id as section_id, h.id as hole_id + from _albion.named_section as s + join _albion.hole as h on s.geom && h.geom and st_intersects(s.geom, st_startpoint(h.geom)) + ) + select st_collectionhomogenize(st_collect(ef.geom)) + from albion.all_edge as e + join hole_idx as hs on hs.hole_id = e.start_ + join hole_idx as he on he.hole_id = e.end_ and he.section_id = hs.section_id + join albion.edge_face as ef on ef.start_ = e.start_ and ef.end_ = e.end_ and not st_isempty(ef.geom) + where ef.graph_id='{}' + """.format( + graph + ) + ) + + drawing = dxf.drawing(filename) + m = wkb.loads(bytes.fromhex(cur.fetchone()[0])) + for p in m: + r = p.exterior.coords + drawing.add( + dxf.face3d([tuple(r[0]), tuple(r[1]), tuple(r[2])], flags=1) + ) + drawing.save() + + + def __srid(self): + with self.connect() as con: + cur = con.cursor() + cur.execute("select srid from albion.metadata") + srid, = cur.fetchone() + return srid + def __getattr__(self, name): - if name == "has_collar": - return self.__has_collar() - elif name == "has_hole": + if name == "has_hole": return self.__has_hole() elif name == "has_section": return self.__has_section() + elif name == "has_volume": + return self.__has_volume() elif name == "has_group_cell": return self.__has_group_cell() + elif name == "has_graph": + return self.__has_graph() elif name == "has_radiometry": return self.__has_radiometry() elif name == "has_cell": return self.__has_cell() elif name == "name": return self.__name + elif name == "srid": + return self.__srid() else: raise AttributeError(name) @@ -203,46 +399,56 @@ def __has_cell(self): with self.connect() as con: cur = con.cursor() cur.execute("select count(1) from albion.cell") - return cur.fetchone()[0] > 1 + return cur.fetchone()[0] > 0 - def __has_collar(self): + def __has_hole(self): with self.connect() as con: cur = con.cursor() - cur.execute("select count(1) from albion.collar") - return cur.fetchone()[0] > 1 + cur.execute("select count(1) from albion.hole where geom is not null") + return cur.fetchone()[0] > 0 - def __has_hole(self): + def __has_volume(self): with self.connect() as con: cur = con.cursor() - cur.execute("select count(1) from albion.hole") - return cur.fetchone()[0] > 1 + cur.execute("select count(1) from albion.volume") + return cur.fetchone()[0] > 0 def __has_section(self): with self.connect() as con: cur = con.cursor() - cur.execute("select count(1) from albion.section_geom") - return cur.fetchone()[0] > 1 + cur.execute("select count(1) from albion.named_section") + return cur.fetchone()[0] > 0 def __has_group_cell(self): with self.connect() as con: cur = con.cursor() cur.execute("select count(1) from albion.group_cell") - return cur.fetchone()[0] > 1 + return cur.fetchone()[0] > 0 + + def __has_graph(self): + with self.connect() as con: + cur = con.cursor() + cur.execute("select count(1) from albion.graph") + return cur.fetchone()[0] > 0 def __has_radiometry(self): with self.connect() as con: cur = con.cursor() cur.execute("select count(1) from albion.radiometry") - return cur.fetchone()[0] > 1 + return cur.fetchone()[0] > 0 + + def import_data(self, dir_, progress=None): + progress = progress if progress is not None else DummyProgress() with self.connect() as con: cur = con.cursor() + cur.execute( """ - copy _albion.collar(id, x, y, z, date_, comments) from '{}' delimiter ';' csv header + copy _albion.hole(id, x, y, z, depth_, date_, comments) from '{}' delimiter ';' csv header """.format( find_in_dir(dir_, "collar") ) @@ -252,29 +458,23 @@ def import_data(self, dir_, progress=None): cur.execute( """ - update _albion.collar set geom=format('SRID=%s;POINTZ(%s %s %s)',m. srid, x, y, z)::geometry - from albion.metadata as m - """ - ) - - cur.execute( - """ - insert into _albion.hole(id, collar_id) select id, id from _albion.collar; - """ + copy _albion.deviation(hole_id, from_, dip, azimuth) from '{}' delimiter ';' csv header + """.format( + find_in_dir(dir_, "devia") + ) ) progress.setPercent(10) cur.execute( """ - copy _albion.deviation(hole_id, from_, dip, azimuth) from '{}' delimiter ';' csv header - """.format( - find_in_dir(dir_, "devia") - ) + update _albion.hole set geom = albion.hole_geom(id) + """ ) progress.setPercent(15) + if find_in_dir(dir_, "avp"): cur.execute( """ @@ -330,91 +530,20 @@ def import_data(self, dir_, progress=None): progress.setPercent(40) - cur.execute( - """ - with dep as ( - select hole_id, max(to_) as mx - from ( - select hole_id, max(to_) as to_ from _albion.radiometry group by hole_id - union all - select hole_id, max(to_) as to_ from _albion.resistivity group by hole_id - union all - select hole_id, max(to_) as to_ from _albion.formation group by hole_id - union all - select hole_id, max(to_) as to_ from _albion.lithology group by hole_id - union all - select hole_id, max(to_) as to_ from _albion.facies group by hole_id - union all - select hole_id, max(from_) as to_ from _albion.deviation group by hole_id - ) as t - group by hole_id - ) - update _albion.hole as h set depth_=d.mx - from dep as d where h.id=d.hole_id - """ - ) - - progress.setPercent(50) - - cur.execute("update albion.hole set geom=albion.hole_geom(id)") - - progress.setPercent(55) - - cur.execute( - "update albion.lithology set geom=albion.hole_piece(from_, to_, hole_id)" - ) - - progress.setPercent(60) - - cur.execute( - "update albion.formation set geom=albion.hole_piece(from_, to_, hole_id)" - ) - - progress.setPercent(65) - - cur.execute( - "update albion.radiometry set geom=albion.hole_piece(from_, to_, hole_id)" - ) - - progress.setPercent(70) - - cur.execute( - "update albion.resistivity set geom=albion.hole_piece(from_, to_, hole_id)" - ) - - progress.setPercent(75) - - cur.execute( - "update albion.facies set geom=albion.hole_piece(from_, to_, hole_id)" - ) - - if find_in_dir(dir_, "mineralization"): - cur.execute( - """ - copy _albion.mineralization(hole_id, level_, from_, to_, oc, accu, grade) from '{}' delimiter ';' csv header - """.format( - find_in_dir(dir_, "mineralization") - ) - ) - - cur.execute( - "update albion.mineralization set geom=albion.hole_piece(from_, to_, hole_id)" - ) - - progress.setPercent(80) - if find_in_dir(dir_, "chemical"): cur.execute( """ copy _albion.chemical(hole_id, from_, to_, num_sample, - element, thickness, gt, grade, equi, comments - ) + element, thickness, gt, grade, equi, comments) from '{}' delimiter ';' csv header """.format( find_in_dir(dir_, "chemical") ) ) + progress.setPercent(45) + + progress.setPercent(100) con.commit() @@ -427,24 +556,6 @@ def triangulate(self): cur.execute("select albion.triangulate()") con.commit() - def refresh_all_edge(self): - with self.connect() as con: - cur = con.cursor() - cur.execute("refresh materialized view albion.all_edge") - con.commit() - - def refresh_radiometry(self): - with self.connect() as con: - cur = con.cursor() - cur.execute("refresh materialized view albion.radiometry_section") - con.commit() - - def refresh_resistivity(self): - with self.connect() as con: - cur = con.cursor() - cur.execute("refresh materialized view albion.resistivity_section") - con.commit() - def create_sections(self): with self.connect() as con: cur = con.cursor() @@ -474,7 +585,7 @@ def new_graph(self, graph, parent=None): cur.execute("insert into albion.graph(id) values ('{}');".format(graph)) con.commit() - def delete_graph(self): + def delete_graph(self, graph): with self.connect() as con: cur = con.cursor() cur.execute("delete from albion.graph cascade where id='{}';".format(graph)) @@ -486,38 +597,8 @@ def previous_section(self, section): cur = con.cursor() cur.execute( """ - select group_id from albion.section where id='{}' - """.format( - section - ) - ) - group, = cur.fetchone() - group = group or 0 - cur.execute( - """ - select group_id, geom from albion.section_geom - where section_id='{section}' - and group_id < {group} - order by group_id desc - limit 1 - """.format( - group=group, section=section - ) - ) - res = cur.fetchone() - if res: - sql = """ - update albion.section set group_id={}, geom='{}'::geometry where id='{}' - """.format( - res[0], res[1], section - ) - else: - sql = """ - update albion.section set group_id=null, geom=albion.first_section(anchor) where id='{}' - """.format( - section - ) - cur.execute(sql) + update albion.section set geom=coalesce(albion.previous_section(%s), geom) where id=%s + """, (section, section)) con.commit() def next_section(self, section): @@ -527,53 +608,99 @@ def next_section(self, section): cur = con.cursor() cur.execute( """ - select group_id from albion.section where id='{}' - """.format( - section - ) - ) - group, = cur.fetchone() - group = group or 0 + update albion.section set geom=coalesce(albion.next_section(%s), geom) where id=%s + """, (section, section)) + con.commit() + + def next_subsection(self, section): + with self.connect() as con: + print("select section from distance") + cur = con.cursor() cur.execute( """ - select group_id, geom from albion.section_geom + select sg.group_id + from albion.section_geom sg + join albion.section s on s.id=sg.section_id + where s.id='{section}' + order by st_distance(s.geom, sg.geom), st_HausdorffDistance(s.geom, sg.geom) asc + limit 1 + """.format(section=section)) + res = cur.fetchone() + if not res: + return + group = res[0] or 0 + print("select geom for next") + cur.execute( + """ + select geom from albion.section_geom where section_id='{section}' and group_id > {group} order by group_id asc - limit 1 - """.format( - group=group, section=section - ) + limit 1 """.format( group=group, section=section) ) res = cur.fetchone() + print("update section") if res: sql = """ - update albion.section set group_id={}, geom='{}'::geometry where id='{}' - """.format( - res[0], res[1], section - ) + update albion.section set geom=st_multi('{}'::geometry) where id='{}' + """.format(res[0], section) cur.execute(sql) con.commit() + print("done") - def next_group_ids(self): + def previous_subsection(self, section): with self.connect() as con: cur = con.cursor() cur.execute( """ - select cell_id from albion.next_group where section_id='{}' - """.format( - self.__current_section.currentText() - ) - ) - return [cell_id for cell_id, in cur.fetchall()] + select sg.group_id + from albion.section_geom sg + join albion.section s on s.id=sg.section_id + where s.id='{section}' + order by st_distance(s.geom, sg.geom), st_HausdorffDistance(s.geom, sg.geom) asc + limit 1 + """.format(section=section)) + res = cur.fetchone() + if not res: + return + group = res[0] or 0 + cur.execute( + """ + select geom from albion.section_geom + where section_id='{section}' + and group_id < {group} + order by group_id desc + limit 1 + """.format(group=group, section=section)) + res = cur.fetchone() + if res: + sql = """ + update albion.section set geom=st_multi('{}'::geometry) where id='{}' + """.format(res[0], section) + cur.execute(sql) + con.commit() + + +# def next_group_ids(self): +# with self.connect() as con: +# cur = con.cursor() +# cur.execute( +# """ +# select cell_id from albion.next_group where section_id='{}' +# """.format( +# self.__current_section.currentText() +# ) +# ) +# return [cell_id for cell_id, in cur.fetchall()] +# def create_group(self, section, ids): with self.connect() as con: # add group cur = con.cursor() cur.execute( """ - insert into albion.group default values returning id + insert into albion.group(id) values ((select coalesce(max(id)+1, 1) from albion.group)) returning id """ ) group, = cur.fetchone() @@ -618,13 +745,9 @@ def compute_mineralization(self, cutoff, ci, oc): oc=oc, ci=ci, cutoff=cutoff ) ) - cur.execute( - """ - update albion.mineralization set geom=albion.hole_piece(from_, to_, hole_id) - where geom is null - """ - ) + cur.execute("refresh materialized view albion.mineralization_section_geom_cache") con.commit() + def export_obj(self, graph_id, filename): with self.connect() as con: @@ -642,60 +765,50 @@ def export_obj(self, graph_id, filename): ) open(filename, "w").write(cur.fetchone()[0]) - def export_elementary_volume_obj(self, graph_id, cell_id, outdir, closed): + def export_elementary_volume_obj(self, graph_id, cell_ids, outdir, closed_only=False): with self.connect() as con: - closed_sql = "" - if not closed: - closed_sql = "not" - cur = con.cursor() cur.execute( """ - select albion.to_obj(geom) from albion.dynamic_volume - where cell_id='{}' and graph_id='{}' - and {} albion.is_closed_volume(geom) + select cell_id, row_number() over(partition by cell_id order by closed desc), obj, closed + from ( + select cell_id, albion.to_obj(triangulation) as obj, albion.is_closed_volume(triangulation) as closed + from albion.volume + where cell_id in ({}) and graph_id='{}' + ) as t """.format( - cell_id, graph_id, closed_sql + ','.join(["'{}'".format(c) for c in cell_ids]), graph_id ) ) - - status = "opened" - if closed: - status = "closed" - - i = 0 - for obj in cur.fetchall(): - filename = '{}_{}_{}_{}.obj'.format(cell_id, graph_id, status, i) - i += 1 + for cell_id, i, obj, closed in cur.fetchall(): + if closed_only and not closed: + continue + filename = '{}_{}_{}_{}.obj'.format(cell_id, graph_id, "closed" if closed else "opened", i) path = os.path.join(outdir, filename) open(path, "w").write(obj[0]) + - def export_elementary_volume_dxf(self, graph_id, cell_id, outdir, closed): + def export_elementary_volume_dxf(self, graph_id, cell_ids, outdir, closed_only=False): with self.connect() as con: - closed_sql = "" - if not closed: - closed_sql = "not" - cur = con.cursor() cur.execute( """ - select geom from albion.dynamic_volume - where cell_id='{}' and graph_id='{}' - and {} albion.is_closed_volume(geom) + select cell_id, row_number() over(partition by cell_id order by closed desc), geom, closed + from ( + select cell_id, triangulation as geom, albion.is_closed_volume(triangulation) as closed + from albion.volume + where cell_id in ({}) and graph_id='{}' + ) as t """.format( - cell_id, graph_id, closed_sql + ','.join(["'{}'".format(c) for c in cell_ids]), graph_id ) ) - status = "opened" - if closed: - status = "closed" - - i = 0 - for wkb_geom in cur.fetchall(): - geom = wkb.loads(bytes.fromhex(wkb_geom[0])) - - filename = '{}_{}_{}_{}.dxf'.format(cell_id, graph_id, status, i) + for cell_id, i, wkb_geom, closed in cur.fetchall(): + geom = wkb.loads(bytes.fromhex(wkb_geom)) + if closed_only and not closed: + continue + filename = '{}_{}_{}_{}.dxf'.format(cell_id, graph_id, "closed" if closed else "opened", i) path = os.path.join(outdir, filename) drawing = dxf.drawing(path) @@ -706,8 +819,6 @@ def export_elementary_volume_dxf(self, graph_id, cell_id, outdir, closed): ) drawing.save() - i += 1 - def errors_obj(self, graph_id, filename): with self.connect() as con: cur = con.cursor() @@ -747,6 +858,64 @@ def export_dxf(self, graph_id, filename): ) drawing.save() + def export_holes_vtk(self, filename): + with self.connect() as con: + cur = con.cursor() + cur.execute( + """ + select albion.to_vtk(st_collect(geom)) + from albion.hole + """ + ) + open(filename, "w").write(cur.fetchone()[0]) + + def export_holes_dxf(self, filename): + with self.connect() as con: + cur = con.cursor() + cur.execute( + """ + select st_collect(geom) + from albion.hole + """ + ) + drawing = dxf.drawing(filename) + m = wkb.loads(bytes.fromhex(cur.fetchone()[0])) + for l in m: + r = l.coords + drawing.add( + dxf.polyline(list(l.coords)) + ) + drawing.save() + + def export_layer_vtk(self, table, filename): + with self.connect() as con: + cur = con.cursor() + cur.execute( + """ + select albion.to_vtk(st_collect(albion.hole_piece(from_, to_, hole_id))) + from albion.{} + """.format(table) + ) + open(filename, "w").write(cur.fetchone()[0]) + + def export_layer_dxf(self, table, filename): + with self.connect() as con: + cur = con.cursor() + cur.execute( + """ + select st_collect(albion.hole_piece(from_, to_, hole_id)) + from albion.{} + """.format(table) + ) + drawing = dxf.drawing(filename) + m = wkb.loads(bytes.fromhex(cur.fetchone()[0])) + for l in m: + r = l.coords + drawing.add( + dxf.polyline(list(l.coords)) + ) + drawing.save() + def create_volumes(self, graph_id): with self.connect() as con: cur = con.cursor() @@ -759,8 +928,8 @@ def create_volumes(self, graph_id): ) cur.execute( """ - insert into albion.volume(graph_id, cell_id, triangulation) - select graph_id, cell_id, geom + insert into _albion.volume(graph_id, cell_id, triangulation, face1, face2, face3) + select graph_id, cell_id, geom, face1, face2, face3 from albion.dynamic_volume where graph_id='{}' and geom is not null --not st_isempty(geom) @@ -782,8 +951,8 @@ def create_terminations(self, graph_id): ) cur.execute( """ - insert into albion.end_node(geom, node_id, collar_id, graph_id) - select geom, node_id, collar_id, graph_id + insert into albion.end_node(geom, node_id, hole_id, graph_id) + select geom, node_id, hole_id, graph_id from albion.dynamic_end_node where graph_id='{}' """.format( @@ -799,6 +968,7 @@ def export(self, filename): def import_(name, filename): import_db(filename, name) project = Project(name) + project.update() project.create_sections() return project @@ -851,10 +1021,18 @@ def create_section_view_0_90(self, z_scale): ) ) - cur.execute("refresh materialized view albion.radiometry_section") - cur.execute("refresh materialized view albion.resistivity_section") + #cur.execute("refresh materialized view albion.radiometry_section") + #cur.execute("refresh materialized view albion.resistivity_section") con.commit() + def refresh_section_geom(self, table): + with self.connect() as con: + cur = con.cursor() + cur.execute("select count(1) from albion.layer where name='{}'".format(table)) + if cur.fetchone()[0]: + cur.execute("refresh materialized view albion.{}_section_geom_cache".format(table)) + con.commit() + def closest_hole_id(self, x, y): with self.connect() as con: cur = con.cursor() @@ -873,7 +1051,7 @@ def closest_hole_id(self, x, y): if not res: cur.execute( """ - select hole_id from albion.current_hole_section + select hole_id from albion.hole_section where st_dwithin(geom, 'SRID={srid} ;POINT({x} {y})'::geometry, 25) order by st_distance('SRID={srid} ;POINT({x} {y})'::geometry, geom) limit 1""".format( @@ -884,6 +1062,21 @@ def closest_hole_id(self, x, y): return res[0] if res else None + def add_named_section(self, section_id, geom): + with self.connect() as con: + cur = con.cursor() + cur.execute("select srid from albion.metadata") + srid, = cur.fetchone() + cur.execute( + """ + insert into albion.named_section(geom, section) + values (ST_SetSRID('{wkb_hex}'::geometry, {srid}), '{section_id}') + """.format( + srid=srid, wkb_hex=geom.wkb_hex, section_id=section_id + ) + ) + con.commit() + def set_section_geom(self, section_id, geom): with self.connect() as con: cur = con.cursor() @@ -891,9 +1084,31 @@ def set_section_geom(self, section_id, geom): srid, = cur.fetchone() cur.execute( """ - update albion.section set geom=ST_SetSRID('{wkb_hex}'::geometry, {srid}) where id='{id_}' + update albion.section set geom=st_multi(ST_SetSRID('{wkb_hex}'::geometry, {srid})) where id='{id_}' """.format( - srid=srid, wkb_hex=binascii.hexlify(geom.wkb), id_=section_id + srid=srid, wkb_hex=geom.wkb_hex, id_=section_id ) ) con.commit() + + def add_to_graph_node(self, graph, features): + with self.connect() as con: + cur = con.cursor() + cur.executemany( + """ + insert into albion.node(from_, to_, hole_id, graph_id) values(%s, %s, %s, %s) + """, + [(f['from_'], f['to_'], f['hole_id'], graph) for f in features]) + + def accept_possible_edge(self, graph): + with self.connect() as con: + cur = con.cursor() + cur.execute( + """ + insert into albion.edge(start_, end_, graph_id, geom) + select start_, end_, graph_id, geom from albion.possible_edge + where graph_id=%s + """, + (graph,)) + + diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..b2b297e --- /dev/null +++ b/requirements.txt @@ -0,0 +1,7 @@ +gitpython +dxfwrite +PyOpenGL +pglite +pyqt5 +psycopg2 +shapely diff --git a/res/next_line_big.svg b/res/next_line_big.svg new file mode 100644 index 0000000..fe34c3c --- /dev/null +++ b/res/next_line_big.svg @@ -0,0 +1,81 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/res/previous_line_big.svg b/res/previous_line_big.svg new file mode 100644 index 0000000..dba587c --- /dev/null +++ b/res/previous_line_big.svg @@ -0,0 +1,81 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/res/template_project.qgs b/res/template_project.qgs index 9a82be1..c394e96 100644 --- a/res/template_project.qgs +++ b/res/template_project.qgs @@ -1,67 +1,166 @@ - + + - + + + + +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs + 3116 + 32632 + EPSG:32632 + WGS 84 / UTM zone 32N + utm + WGS84 + false + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + cell_99e875c2_110e_4f52_a7f4_54ea9cff1367 + collar_7e814841_4cc9_495a_8ef6_9229fcfffb5e + edge_32c6a82a_82cd_477f_b119_398536a6b6ed + edge_section_8b745326_1406_43a8_803e_8ee85be59103 + end_node_section_node_geom_f653243c_2e98_4638_b428_1acca01868dc + group_cell_ec51a010_ab41_49d7_ac55_8ce0cfbb8838 + hole_section_1f8c7d9f_7032_4a97_8033_d7ee1c22ea2a + node_section_63e3816d_ea42_44c6_937c_65c10a3b3ac4 + possible_edge_a6b83b46_56da_4098_b0fe_e703633800f5 + possible_edge_section_04705590_fcdc_4fe9_8309_3d2c648db28f + section_anchor_5a49e7a1_cea4_4a35_982c_00e7801cd6da + section_intersection_13a1d3a9_897b_48fc_9714_e20439ad1ff6 + section_polygon_f49e48f4_9101_4351_a07c_0a99748eaca2 + close_collar_95b0fcfd_f8c9_476f_9b9d_39d3d8c782f7 + section_geom_cbc425ee_c515_4cda_8fec_53f986b4dc2f + named_section_geom_d56ae49a_c71c_485f_84ea_2a788ff48e0b + mineralization_ed8c17ee_2398_4fc0_8deb_6db49e2465d7 + radiometry_9d3b823d_d1b6_47a8_a1c2_dd0895a7262e + resistivity_4ed802a8_e512_490c_bf34_cd284ac03506 + facies_047eef51_ef17_438a_b299_7879992c4e6a + formation_8aad2fae_f596_4e4a_b6c4_dea6ae893731 + mineralization_b5620aba_20dd_42f3_8de8_271f443b4578 + + + + + + + + + + + + + + + + + + + + + + + + + + + - + meters - 322700.65761908068088815 - 2074283.03263000538572669 - 324598.16511556104524061 - 2075541.11146176373586059 + 31738932.63210045173764229 + 33490672.35866895318031311 + 31739689.99440045282244682 + 33491176.27513902634382248 0 - 1 +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs @@ -75,258 +174,139 @@ 0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - collar20170522101211588 - metadata20170602185222421 - current_radiometry_section20171210141136639 - current_mineralization_section20171210141434594 - group_cell20171210141649354 - section_geom20171210141834658 - cell20171210155444151 - current_hole_section20171210181943739 - current_formation_section20171210182253618 - current_node_section20171211135743640 - current_edge_section20171211173954570 - edge20171211174003737 - Google_cn_Satellites20171215210416430 - possible_edge20171219072744468 - section_anchor20171220085838796 - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - -20037508.34278924390673637 - -20037508.34278924390673637 - 20037508.34278924390673637 - 20037508.34278924390673637 - - Google_cn_Satellites20171215210416430 - . - - - - Google.cn Satellites - - - +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs - 3857 - 3857 - EPSG:3857 - WGS 84 / Pseudo Mercator - merc - WGS84 - false - - - - - - - - - - - - - - - - - - - - - 323136.61999999999534339 - 2074866.80000000004656613 - 323587.19000000000232831 - 2075166.59000000008381903 - - cell20171210155444151 - dbname='template_project' host=localhost port=55432 sslmode=disable key='id' srid=32632 type=Polygon table="albion"."cell" (geom) sql= + + cell_99e875c2_110e_4f52_a7f4_54ea9cff1367 + dbname='template_project' host=127.0.0.1 port=55432 sslmode=disable key='"id"' srid=32632 type=Polygon checkPrimaryKeyUnicity='1' table="albion"."cell" (geom) sql= @@ -343,313 +323,179 @@ false + + + + + dataset + + + + + + + + +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs + 3116 + 32632 + EPSG:32632 + WGS 84 / UTM zone 32N + utm + WGS84 + false + + + + postgres + + - - + + - - - - - - - - - - - - - - - + + + 1 + 1 + 1 + + - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + 0 0 - 0 - id - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - . + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - . + + + + + 0 - . - + + 0 generatedlayout + + - - - - - - - - - - + - - - 323136.61999999999534339 - 2074866.80000000004656613 - 323587.19000000000232831 - 2075166.59000000008381903 - - collar20170522101211588 - dbname='template_project' host=localhost port=55432 sslmode=disable key='id' srid=32632 type=PointZ table="albion"."collar" (geom) sql= + + close_collar_95b0fcfd_f8c9_476f_9b9d_39d3d8c782f7 + dbname='template_project' host=127.0.0.1 port=55432 sslmode=disable key='"id"' srid=32632 type=PointZ checkPrimaryKeyUnicity='1' table="albion"."close_collar" (geom) sql= - collar + close_collar +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs @@ -662,271 +508,165 @@ def my_form_open(dialog, layer, feature): false + + + + + dataset + + + + + + + + + + + + + + + + + +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs + 3116 + 32632 + EPSG:32632 + WGS 84 / UTM zone 32N + utm + WGS84 + false + + + + + + + + + + + + postgres + + - - + + - - - - - - - - - - - - + + + 1 + 1 + 1 + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + 0 0 - 0 - id - - - - - - - + 1 + + + + - - - - - - - - - - - - - - - - - - - - - - - - . + + + + + + + + + + + + + + + + + - - - - + + + + + + + + + + + + + + - - . + + + + + 0 - . + 0 generatedlayout + + + + + + - - - - - - - - - - COALESCE( "id", '<NULL>' ) + id + - + - 323137.12120096100261435 - 2074749.36095459992066026 - 323737.77071975398575887 - 2075166.65021662996150553 + 31739063.62000000104308128 + 33490793.80000000074505806 + 31739514.19000000134110451 + 33491093.58999999985098839 - current_edge_section20171211173954570 - dbname='template_project' host=localhost port=55432 sslmode=disable key='id' srid=32632 type=LineString table="albion"."current_edge_section" (geom) sql= + collar_7e814841_4cc9_495a_8ef6_9229fcfffb5e + dbname='template_project' host=127.0.0.1 port=55432 sslmode=disable key='"id"' srid=32632 type=PointZ checkPrimaryKeyUnicity='1' table="albion"."collar" (geom) sql= - current_edge_section + collar +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs @@ -982,386 +720,243 @@ def my_form_open(dialog, layer, feature): false + + + + + dataset + + + + + + + + + + + + + + + + + +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs + 3116 + 32632 + EPSG:32632 + WGS 84 / UTM zone 32N + utm + WGS84 + false + + + + + + + + + + + + postgres + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - + + + 1 + 1 + 1 + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + 0 0 - 0 - id - - - - - - - + 1 + + + + - - - - - - - - - - - - - - - - - - - - - - - - . + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + - - + + + + + + + + + + + + + + + + + + + + + + + - - . + + + + + 0 - . + 0 generatedlayout + + + + + + + + + + + + - - - - - - - - - - - - - COALESCE( "id", '<NULL>' ) + id + - - current_formation_section20171210182253618 - dbname='template_project' host=localhost port=55432 sslmode=disable key='id' srid=32632 type=LineString table="albion"."current_formation_section" (geom) sql= + + edge_32c6a82a_82cd_477f_b119_398536a6b6ed + dbname='template_project' host=127.0.0.1 port=55432 sslmode=disable key='"id"' srid=32632 type=LineStringZ checkPrimaryKeyUnicity='1' table="albion"."edge" (geom) sql= - current_formation_section + edge +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs @@ -1374,487 +969,173 @@ def my_form_open(dialog, layer, feature): false + + + + + dataset + + + + + + + + +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs + 3116 + 32632 + EPSG:32632 + WGS 84 / UTM zone 32N + utm + WGS84 + false + + + + postgres + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + 1 + 1 + 1 + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + 0 0 - 0 - id - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - . + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + - . + + + + + 0 - . - + + 0 generatedlayout + + - - - - - - - - - - - - + - - current_hole_section20171210181943739 - dbname='template_project' host=localhost port=55432 sslmode=disable key='id' srid=32632 type=LineString table="albion"."current_hole_section" (geom) sql= + + edge_section_8b745326_1406_43a8_803e_8ee85be59103 + dbname='template_project' host=127.0.0.1 port=55432 sslmode=disable key='"id"' srid=32632 type=LineString checkPrimaryKeyUnicity='1' table="albion"."edge_section" (geom) sql= - current_hole_section + edge_section +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs @@ -1867,130 +1148,2007 @@ def my_form_open(dialog, layer, feature): false + + + + + dataset + + + + + + + + + + + + + + + + + +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs + 3116 + 32632 + EPSG:32632 + WGS 84 / UTM zone 32N + utm + WGS84 + false + + + + + + + + + + + + postgres + + - - + + - - - - - - - - - - - - - - - - - - + + + 1 + 1 + 1 + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - + - - + + + + + 0 0 - 0 - id - - - - . + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + 0 + generatedlayout + + + + + + + + + + + + + + + + + + id + + + + end_node_section_node_geom_f653243c_2e98_4638_b428_1acca01868dc + dbname='template_project' host=127.0.0.1 port=55432 sslmode=disable key='"id"' srid=32632 type=LineString checkPrimaryKeyUnicity='1' table="albion"."end_node_section" (node_geom) sql= + + + + end_node_section.node_geom + + + +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs + 3116 + 32632 + EPSG:32632 + WGS 84 / UTM zone 32N + utm + WGS84 + false + + + + + + + dataset + + + + + + + + + + + + + + + + + +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs + 3116 + 32632 + EPSG:32632 + WGS 84 / UTM zone 32N + utm + WGS84 + false + + + + + + + + + + + + + postgres + + + + + + + + + + + 1 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + 0 + generatedlayout + + + + + + + + + + + + + + + + + + id + + + + facies_047eef51_ef17_438a_b299_7879992c4e6a + dbname='template_project' host=127.0.0.1 port=55432 sslmode=disable key='"id"' srid=32632 type=PointZ checkPrimaryKeyUnicity='1' table="albion"."facies" (geom) sql= + + + + facies + + + +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs + 3116 + 32632 + EPSG:32632 + WGS 84 / UTM zone 32N + utm + WGS84 + false + + + + + + + dataset + + + + + + + + +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs + 3116 + 32632 + EPSG:32632 + WGS 84 / UTM zone 32N + utm + WGS84 + false + + + + + postgres + + + + + + + + + + + 1 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + 0 + generatedlayout + + + + + + + + + 31739063.62000000104308128 + 33490793.80000000074505806 + 31739514.19000000134110451 + 33491093.58999999985098839 + + formation_8aad2fae_f596_4e4a_b6c4_dea6ae893731 + dbname='template_project' host=127.0.0.1 port=55432 sslmode=disable key='"id"' srid=32632 type=PointZ checkPrimaryKeyUnicity='1' table="albion"."formation" (geom) sql= + + + + formation + + + +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs + 3116 + 32632 + EPSG:32632 + WGS 84 / UTM zone 32N + utm + WGS84 + false + + + + + + + dataset + + + + + + + + +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs + 3116 + 32632 + EPSG:32632 + WGS 84 / UTM zone 32N + utm + WGS84 + false + + + + + postgres + + + + + + + + + + + 1 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + 0 + generatedlayout + + + + + + + + group_cell_ec51a010_ab41_49d7_ac55_8ce0cfbb8838 + dbname='template_project' host=127.0.0.1 port=55432 sslmode=disable key='id' srid=32632 type=Polygon checkPrimaryKeyUnicity='1' table="albion"."group_cell" (geom) sql=section_id='SN x4' + + + + group_cell + + + +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs + 3116 + 32632 + EPSG:32632 + WGS 84 / UTM zone 32N + utm + WGS84 + false + + + + + + + dataset + + + + + + + + +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs + 3116 + 32632 + EPSG:32632 + WGS 84 / UTM zone 32N + utm + WGS84 + false + + + + + postgres + + + + + + + + + + + 1 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + 0 + generatedlayout + + + + + + + + hole_section_1f8c7d9f_7032_4a97_8033_d7ee1c22ea2a + dbname='template_project' host=127.0.0.1 port=55432 sslmode=disable key='"id"' srid=32632 type=LineString checkPrimaryKeyUnicity='1' table="albion"."hole_section" (geom) sql= + + + + hole_section + + + +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs + 3116 + 32632 + EPSG:32632 + WGS 84 / UTM zone 32N + utm + WGS84 + false + + + + + + + dataset + + + + + + + + +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs + 3116 + 32632 + EPSG:32632 + WGS 84 / UTM zone 32N + utm + WGS84 + false + + + + + postgres + + + + + + + + + + + 1 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + 0 + generatedlayout + + + + + + + + metadata_c78c898c_772f_4524_8e45_76de4e0463d3 + dbname='template_project' host=127.0.0.1 port=55432 sslmode=disable key='"id"' checkPrimaryKeyUnicity='1' table="albion"."metadata" sql= + + + + metadata + + + + 0 + 0 + + + + + false + + + + + + + dataset + + + + + + + + + 0 + 0 + + + + + false + + + + + postgres + + + + + + + + + + + 1 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + 0 + generatedlayout + + + + + + + + + 31739088.89999999850988388 + 33490793.80000000074505806 + 31739505.76999999955296516 + 33491092.85000000149011612 + + mineralization_b5620aba_20dd_42f3_8de8_271f443b4578 + dbname='template_project' host=127.0.0.1 port=55432 sslmode=disable key='"id"' srid=32632 type=PointZ checkPrimaryKeyUnicity='1' table="albion"."mineralization" (geom) sql= + + + + mineralization + + + +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs + 3116 + 32632 + EPSG:32632 + WGS 84 / UTM zone 32N + utm + WGS84 + false + + + + + + + dataset + + + + + + + + +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs + 3116 + 32632 + EPSG:32632 + WGS 84 / UTM zone 32N + utm + WGS84 + false + + + + + postgres + + + + + + + + + + + 1 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + 0 + generatedlayout + + + + + + + + mineralization_ed8c17ee_2398_4fc0_8deb_6db49e2465d7 + dbname='template_project' host=127.0.0.1 port=55432 sslmode=disable key='"id"' srid=32632 type=PointZ checkPrimaryKeyUnicity='1' table="albion"."mineralization" (geom) sql= + + + + mineralization + + + +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs + 3116 + 32632 + EPSG:32632 + WGS 84 / UTM zone 32N + utm + WGS84 + false + + + + + + + dataset + + + + + + + + +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs + 3116 + 32632 + EPSG:32632 + WGS 84 / UTM zone 32N + utm + WGS84 + false + + + + + postgres + + + + + + + + + + + 1 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - . + + + + + 0 - . + 0 generatedlayout + + - - - - - - - - - - - + - - - 323288.30356994899921119 - 2074730.4480744400061667 - 323738.71807432197965682 - 2075067.08355449000373483 - - current_mineralization_section20171210141434594 - dbname='template_project' host=localhost port=55432 sslmode=disable key='id' srid=32632 type=LineString table="albion"."current_mineralization_section" (geom) sql= + + named_section_geom_d56ae49a_c71c_485f_84ea_2a788ff48e0b + dbname='template_project' host=127.0.0.1 port=55432 sslmode=disable key='"id"' srid=32632 type=LineString checkPrimaryKeyUnicity='1' table="albion"."named_section" (geom) sql= - current_mineralization_section + named_section.geom +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs @@ -2003,405 +3161,329 @@ def my_form_open(dialog, layer, feature): false + + + + + dataset + + + + + + + + + + + + + + + + + +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs + 3116 + 32632 + EPSG:32632 + WGS 84 / UTM zone 32N + utm + WGS84 + false + + + + + + + + + + + + postgres + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + 1 + 1 + 1 + + - - - + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + 0 - 1 - 0 - id - - - - - - - + 0 + 1 + + + + - - - - - - - - - - - - - - - - - - - - - - - - . + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - + + + - - + + + + + + + + + + + + + + + + + + + + + + + - - . + + + + + 0 - . + 0 generatedlayout + + + + + + + + + + + + - - - - - - - - - - - - - - - + id + - - - 323137.3236906590173021 - 2074746.90201969002373517 - 323741.30310256197117269 - 2075166.57410508999601007 - - current_node_section20171211135743640 - dbname='template_project' host=localhost port=55432 sslmode=disable key='id' srid=32632 type=LineString table="albion"."current_node_section" (geom) sql= + + node_section_63e3816d_ea42_44c6_937c_65c10a3b3ac4 + dbname='template_project' host=127.0.0.1 port=55432 sslmode=disable key='"id"' srid=32632 type=LineString checkPrimaryKeyUnicity='1' table="albion"."node_section" (geom) sql= - current_node_section + node_section +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs @@ -2414,399 +3496,294 @@ def my_form_open(dialog, layer, feature): false + + + + + dataset + + + + + + + + + + + + + + + + + +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs + 3116 + 32632 + EPSG:32632 + WGS 84 / UTM zone 32N + utm + WGS84 + false + + + + + + + + + + + + postgres + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + 1 + 1 + 1 + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + 0 - 4 - 0 - id - - - - - - - + 0 + 1 + + + + - - - - - - - - - - - - - - - - - - - - - - - - . + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - . + + + + + 0 - . + 0 generatedlayout + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - COALESCE( "id", '<NULL>' ) + id + - - - 323136.99737213598564267 - 2074730.70236835000105202 - 323784.47420165798394009 - 2075092.06782471993938088 - - current_radiometry_section20171210141136639 - dbname='template_project' host=localhost port=55432 sslmode=disable key='id' srid=32632 type=LineString table="albion"."current_radiometry_section" (geom) sql= + + possible_edge_a6b83b46_56da_4098_b0fe_e703633800f5 + dbname='template_project' host=127.0.0.1 port=55432 sslmode=disable key='"id"' srid=32632 type=LineStringZ checkPrimaryKeyUnicity='1' table="albion"."possible_edge" (geom) sql= - current_radiometry_section + possible_edge +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs @@ -2819,327 +3796,173 @@ def my_form_open(dialog, layer, feature): false + + + + + dataset + + + + + + + + +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs + 3116 + 32632 + EPSG:32632 + WGS 84 / UTM zone 32N + utm + WGS84 + false + + + + postgres + + - - + + - - - - - - - - - - - - - - - - - - + + + 1 + 1 + 1 + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + 0 0 - 0 - id - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - . + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + - . + + + + + 0 - . - + + 0 generatedlayout + + - - - - - - - - - - - + - - - 323136.50877837499137968 - 2074866.45427859993651509 - 323588.2962775370106101 - 2075165.99685759004205465 - - edge20171211174003737 - dbname='template_project' host=localhost port=55432 sslmode=disable key='id' srid=32632 type=LineStringZ table="albion"."edge" (geom) sql= + + possible_edge_section_04705590_fcdc_4fe9_8309_3d2c648db28f + dbname='template_project' host=127.0.0.1 port=55432 sslmode=disable key='"id"' srid=32632 type=LineString checkPrimaryKeyUnicity='1' table="albion"."possible_edge_section" (geom) sql= - edge + possible_edge_section +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs @@ -3152,381 +3975,388 @@ def my_form_open(dialog, layer, feature): false + + + + + dataset + + + + + + + + +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs + 3116 + 32632 + EPSG:32632 + WGS 84 / UTM zone 32N + utm + WGS84 + false + + + + postgres + + - - + + - - - - - - - - - - - - - - - - - - - - + + + 1 + 1 + 1 + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + 0 0 - 0 - id - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - . + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - . + + + + + 0 - . - + + 0 generatedlayout + + + + + + + + 31739063.62000000104308128 + 33490793.80000000074505806 + 31739514.19000000134110451 + 33491093.58999999985098839 + + radiometry_9d3b823d_d1b6_47a8_a1c2_dd0895a7262e + dbname='template_project' host=127.0.0.1 port=55432 sslmode=disable key='"id"' srid=32632 type=PointZ checkPrimaryKeyUnicity='1' table="albion"."radiometry" (geom) sql= + + + + radiometry + + + +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs + 3116 + 32632 + EPSG:32632 + WGS 84 / UTM zone 32N + utm + WGS84 + false + + + + + + + dataset + + + + + + + + +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs + 3116 + 32632 + EPSG:32632 + WGS 84 / UTM zone 32N + utm + WGS84 + false + + + + + postgres + + + + + + + + + + + 1 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - COALESCE( "id", '<NULL>' ) + + + 0 + + + 0 + generatedlayout + + + + + - + - 323136.61999999999534339 - 2074866.80000000004656613 - 323587.19000000000232831 - 2075166.59000000008381903 + 31739063.62000000104308128 + 33490793.80000000074505806 + 31739514.19000000134110451 + 33491093.58999999985098839 - group_cell20171210141649354 - dbname='template_project' host=localhost port=55432 sslmode=disable key='id' srid=32632 type=Polygon table="albion"."group_cell" (geom) sql=section_id='WE' + resistivity_4ed802a8_e512_490c_bf34_cd284ac03506 + dbname='template_project' host=127.0.0.1 port=55432 sslmode=disable key='"id"' srid=32632 type=PointZ checkPrimaryKeyUnicity='1' table="albion"."resistivity" (geom) sql= - group_cell + resistivity +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs @@ -3539,475 +4369,360 @@ def my_form_open(dialog, layer, feature): false + + + + + dataset + + + + + + + + +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs + 3116 + 32632 + EPSG:32632 + WGS 84 / UTM zone 32N + utm + WGS84 + false + + + + postgres + + - - + + - - - - - - - - - - - - - - - - - - - - + + + 1 + 1 + 1 + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + 0 0 - 0 - id - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - . + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - . + + + + + 0 - . - + + 0 generatedlayout + + - - - - - - - - - - - COALESCE( "id", '<NULL>' ) + + - - metadata20170602185222421 - dbname='template_project' host=localhost port=55432 sslmode=disable key='id' table="albion"."metadata" sql= + + + 31739063.30036709830164909 + 33490657.7391928993165493 + 31739651.99177160114049911 + 33491093.58999999985098839 + + section_anchor_5a49e7a1_cea4_4a35_982c_00e7801cd6da + dbname='template_project' host=127.0.0.1 port=55432 sslmode=disable key='"id"' srid=32632 type=LineString checkPrimaryKeyUnicity='1' table="albion"."section" (anchor) sql= - metadata + section.anchor - +proj=longlat +datum=WGS84 +no_defs - 3452 - 4326 - EPSG:4326 - WGS 84 - longlat + +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs + 3116 + 32632 + EPSG:32632 + WGS 84 / UTM zone 32N + utm WGS84 - true + false - + + + + + dataset + + + + + + + + +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs + 3116 + 32632 + EPSG:32632 + WGS 84 / UTM zone 32N + utm + WGS84 + false + + + + postgres + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - . + + + 1 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 1 + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - + + - - - - + + + + + + + + + + + + + + + + + + + + + - . + + + + + 0 - . + 0 generatedlayout + + - - - - - - - - - - - - - - - - - COALESCE("id", '<NULL>') + + - - possible_edge20171219072744468 - dbname='template_project' host=localhost port=55432 sslmode=disable key='id' srid=32632 type=LineStringZ table="albion"."possible_edge" (geom) sql= + + section_geom_cbc425ee_c515_4cda_8fec_53f986b4dc2f + dbname='template_project' host=127.0.0.1 port=55432 sslmode=disable key='"id"' srid=32632 type=MultiLineString checkPrimaryKeyUnicity='1' table="albion"."section" (geom) sql= - possible_edge + section.geom +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs @@ -4020,381 +4735,318 @@ def my_form_open(dialog, layer, feature): false + + + + + dataset + + + + + + + + + + + + + + + + + +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs + 3116 + 32632 + EPSG:32632 + WGS 84 / UTM zone 32N + utm + WGS84 + false + + + + + + + + + + + + postgres + + - - + + - - - - - - - - - - - - - - - - - - + + + 1 + 1 + 1 + + - - - + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + 0 0 - 0 - id - - - - - - - + 1 + + + + - - - - - - - - - - - - - - - - - - - - - - - - . + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + - - + + + + + + + + + + + + + + + + + + + + - - . + + + + + 0 - . + 0 generatedlayout + + + + + + + + + + + + - - - - - - - - - - - - COALESCE( "id", '<NULL>' ) + id + - - section_anchor20171220085838796 - dbname='template_project' host=localhost port=55432 sslmode=disable key='id' srid=32632 type=LineString table="albion"."section" (anchor) sql= + + section_intersection_13a1d3a9_897b_48fc_9714_e20439ad1ff6 + dbname='template_project' host=127.0.0.1 port=55432 sslmode=disable key='"id"' srid=32632 type=Polygon checkPrimaryKeyUnicity='1' table="albion"."section_intersection" (geom) sql= - section.anchor + section_intersection +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs @@ -4407,125 +5059,193 @@ def my_form_open(dialog, layer, feature): false + + + + + dataset + + + + + + + + + + + + + + + + + +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs + 3116 + 32632 + EPSG:32632 + WGS 84 / UTM zone 32N + utm + WGS84 + false + + + + + + + + + + + + postgres + + - - + + - - - - - - - - - - - - - - - + + + 1 + 1 + 1 + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - + - - + + + + + 0 0 - 0 - id - - - - + 1 + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + + + + + + + + + + + - + + + + + 0 - + 0 generatedlayout + + + + + + - - - - - - - - - - - + id + - - - 323136.61999999999534339 - 2074912.64999999990686774 - 323587.19000000000232831 - 2075090.92999999993480742 - - section_geom20171210141834658 - dbname='template_project' host=localhost port=55432 sslmode=disable key='id' srid=32632 type=LineString table="albion"."section" (geom) sql= + + section_polygon_f49e48f4_9101_4351_a07c_0a99748eaca2 + dbname='template_project' host=127.0.0.1 port=55432 sslmode=disable key='"id"' srid=32632 type=MultiPolygon checkPrimaryKeyUnicity='1' table="albion"."section_polygon" (geom) sql= - section.geom + section_polygon +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs @@ -4538,487 +5258,235 @@ def my_form_open(dialog, layer, feature): false + + + + + dataset + + + + + + + + +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs + 3116 + 32632 + EPSG:32632 + WGS 84 / UTM zone 32N + utm + WGS84 + false + + + + postgres + + - - + + - - - - - - - - - - - - - - - - - - - - + + + 1 + 1 + 1 + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + 0 0 - 0 - id - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - . + 1 + + + + + + + + + + + + + + + + + + + + + + + + - - - + + - - - - + + + + + + + + + + + + + + + + + + + + + - . + + + + + 0 - . - + + 0 generatedlayout + + - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + false + true + false + 16 + 50 + 30 + false + 0 + 0 + + + WGS84 + + + 255 + 0 + 255 + 255 + 255 + 255 + 255 + template_project - /home/vmo/areva/albion 32632 - dbname=template_project host=localhost port=55432 + /home/vmo/albion/nt meters m2 - - +proj=longlat +datum=WGS84 +no_defs - EPSG:4326 - 3452 - 1 - false - - 0 - 255 - 255 - 255 - 255 - 255 - 255 - - - 1 - - cell20171210155444151 - collar20170522101211588 - current_edge_section20171211173954570 - current_formation_section20171210182253618 - current_hole_section20171210181943739 - current_mineralization_section20171210141434594 - current_node_section20171211135743640 - current_radiometry_section20171210141136639 - edge20171211174003737 - group_cell20171210141649354 - possible_edge20171219072744468 - section_geom20171210141834658 - section_anchor20171220084145458 - - - disabled - disabled - disabled - disabled - disabled - disabled - disabled - disabled - disabled - disabled - disabled - disabled - disabled - - current_layer - - - 2 - 2 - 2 - 2 - 2 - 2 - 2 - 2 - 2 - 2 - 2 - 2 - 2 - - - to_vertex_and_segment - to_vertex_and_segment - to_vertex_and_segment - to_vertex_and_segment - to_vertex_and_segment - to_vertex_and_segment - to_vertex_and_segment - to_vertex_and_segment - to_vertex_and_segment - to_vertex_and_segment - to_vertex_and_segment - to_vertex_and_segment - to_vertex_and_segment - - off - 10 - - 0.000000 - 0.000000 - 0.000000 - 0.000000 - 0.000000 - 0.000000 - 0.000000 - 0.000000 - 0.000000 - 0.000000 - 0.000000 - 0.000000 - 0.000000 - - + + 1 + - 2 true + 2 false + + + + + + + + + + + 2019-08-23T18:21:56 + + + diff --git a/test.py b/test.py index b6b7dec..a41dee1 100644 --- a/test.py +++ b/test.py @@ -1,3 +1,4 @@ +from __future__ import print_function # coding = utf-8 if __name__ == "__main__": @@ -42,7 +43,8 @@ ) as t """) for rec in cur.fetchall(): - print rec + # fix_print_with_import + print(rec) #con = project.connect() #cur = con.cursor() diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/__main__.py b/test/__main__.py new file mode 100644 index 0000000..59baf84 --- /dev/null +++ b/test/__main__.py @@ -0,0 +1,6 @@ +import os +import re + +for f in os.listdir(os.path.dirname(__file__)): + if re.match('^[a-z].*\.py', f): + print(f) diff --git a/test/end_node.py b/test/end_node.py new file mode 100644 index 0000000..7ed3072 --- /dev/null +++ b/test/end_node.py @@ -0,0 +1,63 @@ +from __future__ import print_function +# coding = utf-8 + + +SQL = """ +INSERT INTO albion.collar(id, geom, depth_) VALUES + (1, 'SRID=32632;POINT(0 0 0)'::geometry, 100), + (2, 'SRID=32632;POINT(100 0 0)'::geometry, 100), + (3, 'SRID=32632;POINT(200 0 0)'::geometry, 100), + (4, 'SRID=32632;POINT(300 0 0)'::geometry, 100), + (5, 'SRID=32632;POINT(0 100 0)'::geometry, 100) +; +SELECT albion.triangulate() +; + +INSERT INTO albion.graph(id) VALUES + ('graph1') +; +INSERT INTO albion.node(id, graph_id, hole_id, from_, to_, geom) VALUES + (1, 'graph1', 2, 50, 100, albion.hole_piece(50, 100, '2')), + (2, 'graph1', 3, 0, 50, albion.hole_piece(0, 50, '3')) +; +INSERT INTO albion.edge(id, graph_id, start_, end_, geom) VALUES + (1, 'graph1', 1, 2, 'SRID=32632;LINESTRING(100 0 -75, 200 0 -25)'::geometry) +; +""" + + +if __name__ == "__main__": + from albion.project import Project + import os + import sys + import time + + + project_name = "end_node_test" + + if Project.exists(project_name): + Project.delete(project_name) + + project = Project.create(project_name, 32632) + start = time.time() + + with project.connect() as con: + cur = con.cursor() + for sql in SQL.split("\n;\n")[:-1]: + cur.execute(sql) + con.commit() + + cur.execute(""" + SELECT node_id, hole_id, st_astext(geom) + FROM albion.dynamic_end_node + WHERE hole_id !='5' + """) + for r in cur.fetchall(): + print(r) + + + + + + + diff --git a/test/export.py b/test/export.py new file mode 100644 index 0000000..0aa3626 --- /dev/null +++ b/test/export.py @@ -0,0 +1,19 @@ +if __name__ == "__main__": + + from albion.project import Project + import os + import shutil + + project = Project("tutorial_test") + + with project.connect() as con: + cur = con.cursor() + cur.execute("select id from albion.cell") + if os.path.isdir('/tmp/min1000'): + shutil.rmtree('/tmp/min1000') + os.mkdir('/tmp/min1000') + cells = [r for r, in cur.fetchall()] + project.export_elementary_volume_obj('min1000', cells, '/tmp/min1000') + project.export_elementary_volume_dxf('min1000', cells, '/tmp/min1000') + + diff --git a/test/import.py b/test/import.py new file mode 100644 index 0000000..fa7e52e --- /dev/null +++ b/test/import.py @@ -0,0 +1,29 @@ +from __future__ import print_function +# coding = utf-8 + +if __name__ == "__main__": + from albion.project import Project + import os + import sys + import time + import tempfile + import zipfile + + + + project_name = "import_test" + + if Project.exists(project_name): + Project.delete(project_name) + + project = Project.create(project_name, 32632) + start = time.time() + zip_ref = zipfile.ZipFile(os.path.join(os.path.dirname(__file__), '..', 'data', 'nt.zip'), 'r') + zip_ref.extractall(tempfile.gettempdir()) + zip_ref.close() + data_dir = os.path.join(tempfile.gettempdir(), 'nt') + print('###############################', data_dir) + project.import_data(data_dir) + project.triangulate() + project.create_section_view_0_90(4) + diff --git a/test/mineralisation.py b/test/mineralisation.py new file mode 100644 index 0000000..92f6a72 --- /dev/null +++ b/test/mineralisation.py @@ -0,0 +1,38 @@ + +""" +create table albion.test_mineralization( + id varchar primary key default _albion.unique_id()::varchar, + hole_id varchar not null references _albion.hole(id) on delete cascade on update cascade, + level_ real, + from_ real, + to_ real, + oc real, + accu real, + grade real, + comments varchar, + geom geometry('LINESTRINGZ', $SRID)) +; + +insert into albion.test_mineralization(hole_id, level_, from_, to_, oc, accu, grade) +select hole_id, (t.r).level_, (t.r).from_, (t.r).to_, (t.r).oc, (t.r).accu, (t.r).grade +from ( +select hole_id, albion.segmentation( + array_agg(gamma order by from_),array_agg(from_ order by from_), array_agg(to_ order by from_), + 1., 1., 10) as r +from _albion.radiometry +group by hole_id +) as t +; + +update albion.test_mineralization set geom=albion.hole_piece(from_, to_, hole_id) +; + +create view albion.current_test_mineralization_section as +select row_number() over() as id, m.hole_id, h.collar_id, m.level_, m.oc, m.accu, m.grade, s.id as section_id, + (albion.to_section(m.geom, s.anchor, s.scale))::geometry('LINESTRING', $SRID) as geom +from albion.test_mineralization as m +join _albion.hole as h on h.id=m.hole_id +join _albion.collar as c on c.id=h.collar_id +join _albion.section as s on st_intersects(s.geom, c.geom) +; +""" diff --git a/test/parent_graph.py b/test/parent_graph.py new file mode 100644 index 0000000..7515ebf --- /dev/null +++ b/test/parent_graph.py @@ -0,0 +1,95 @@ +from __future__ import print_function +# coding = utf-8 + +# (1)<--------------100 m----------------->(2) +# 6||3 +# +# +# +# _ -50m +# | 5||2 +# | +# 1||4 +# | +# | _ -100 m + +SQL = """ +INSERT INTO albion.collar(id, geom, depth_) VALUES + (1, 'SRID=32632;POINT(0 0 0)'::geometry, 100), + (2, 'SRID=32632;POINT(100 0 0)'::geometry, 100), + (3, 'SRID=32632;POINT(100 100 0)'::geometry, 100) +; +INSERT INTO albion.cell(id, a, b, c, geom) VALUES + (1, 1, 2, 3, 'SRID=32632;POLYGON((0 0, 100 0, 100 100, 0 0))'::geometry) +; +REFRESH MATERIALIZED VIEW albion.all_edge +; +INSERT INTO albion.graph(id) VALUES + ('graph1') +; +INSERT INTO albion.node(id, graph_id, hole_id, from_, to_, geom) VALUES + (1, 'graph1', 1, 50, 100, 'SRID=32632;LINESTRING(0 0 -50, 0 0 -100)'::geometry), + (2, 'graph1', 2, 50, 60, 'SRID=32632;LINESTRING(100 0 -50, 100 0 -60)'::geometry), + (3, 'graph1', 2, 0, 50, 'SRID=32632;LINESTRING(100 0 -40, 100 0 -50)'::geometry) +; +INSERT INTO albion.graph(id, parent) VALUES + ('graph2', 'graph1') +; +INSERT INTO albion.node(id, graph_id, hole_id, from_, to_, geom) VALUES + (4, 'graph2', 1, 70, 80, 'SRID=32632;LINESTRING(0 0 -70, 0 0 -80)'::geometry), + (5, 'graph2', 2, 50, 60, 'SRID=32632;LINESTRING(100 0 -50, 100 0 -60)'::geometry), + (6, 'graph2', 2, 0, 10, 'SRID=32632;LINESTRING(100 0 0, 100 0 -10)'::geometry) +; +; +""" + + +if __name__ == "__main__": + from albion.project import Project + import os + import sys + import time + + + project_name = "parent_graph_test" + + if Project.exists(project_name): + Project.delete(project_name) + + project = Project.create(project_name, 32632) + start = time.time() + + with project.connect() as con: + cur = con.cursor() + for sql in SQL.split("\n;\n")[:-1]: + cur.execute(sql) + con.commit() + + cur.execute(""" + UPDATE albion.metadata SET correlation_angle=1 ; + """) + + cur.execute(""" + SELECT start_, end_ FROM albion.possible_edge + """) + for s,e in cur.fetchall(): + print(s,e) + + cur.execute(""" + INSERT INTO albion.edge(start_, end_, graph_id, geom) SELECT start_, end_, graph_id, geom FROM albion.possible_edge + """) + + cur.execute(""" + SELECT start_, end_ FROM albion.possible_edge + """) + + edges = [(s,e) for s,e in cur.fetchall()] + print(edges) + assert(('1','2') in edges) + assert(('4','5') in edges) + + + + + + diff --git a/test/tutorial.py b/test/tutorial.py new file mode 100644 index 0000000..c84aa3c --- /dev/null +++ b/test/tutorial.py @@ -0,0 +1,111 @@ +from __future__ import print_function +# coding = utf-8 + +if __name__ == "__main__": + from albion.project import Project + import os + import sys + import time + import tempfile + import zipfile + from osgeo import ogr + + project_name = "tutorial_test" + + if Project.exists(project_name): + Project.delete(project_name) + + project = Project.create(project_name, 32632) + start = time.time() + zip_ref = zipfile.ZipFile(os.path.join(os.path.dirname(__file__), '..', 'data', 'nt.zip'), 'r') + zip_ref.extractall(tempfile.gettempdir()) + zip_ref.close() + data_dir = os.path.join(tempfile.gettempdir(), 'nt') + project.import_data(data_dir) + project.create_section_view_0_90(4) + project.compute_mineralization(1000, 1, 1) + + # import named section from file + with project.connect() as con: + driver = ogr.GetDriverByName('GPKG') + dataSource = driver.Open(os.path.join(os.path.dirname(__file__), 'tutorial_named_section.gpkg'), 0) + data = [(feature.GetField('section'), feature.GetGeometryRef().ExportToWkb().hex()) + for feature in dataSource.GetLayer()] + cur = con.cursor() + cur.execute("delete from albion.named_section") + cur.executemany(""" + insert into albion.named_section(section, geom) + values (%s, st_setsrid(%s::geometry, 32632)) + """, data) + con.commit() + + + project.triangulate() + + with project.connect() as con: + cur = con.cursor() + cur.execute("delete from albion.cell where aspect_ratio > 10") + con.commit() + + with project.connect() as con: + cur = con.cursor() + cur.execute("select name from albion.layer") + layers = [r[0] for r in cur.fetchall()] + print(layers) + + #for l in layers: + # project.refresh_section_geom(l) + + + project.new_graph('330') + project.new_graph('min1000', '330') + + with project.connect() as con: + cur = con.cursor() + cur.execute(""" + insert into albion.node(from_, to_, hole_id, graph_id) + select from_, to_, hole_id, '330' from albion.formation where code=330 + """) + con.commit() + + project.accept_possible_edge('330') + project.create_terminations('330') + project.create_volumes('330') + + # test that all volumes are closed and positive + with project.connect() as con: + cur = con.cursor() + cur.execute(""" + select cell_id from albion.volume where not albion.is_closed_volume(triangulation) + """ + ) + unclosed = cur.fetchall() + if len(unclosed): + print("unclosed volume for cells", unclosed) + assert(len(unclosed) == 0) + cur.execute(""" + select count(1) from albion.volume where albion.volume_of_geom(triangulation) <= 0 + """ + ) + assert(cur.fetchone()[0]==0) + + + with project.connect() as con: + cur = con.cursor() + cur.execute(""" + insert into albion.node(from_, to_, hole_id, graph_id) + select from_, to_, hole_id, 'min1000' from albion.mineralization + """) + + con.commit() + + project.accept_possible_edge('min1000') + project.create_terminations('min1000') + project.create_volumes('min1000') + + project.export_sections_obj('min1000', '/tmp/min1000_section.obj') + project.export_sections_obj('330', '/tmp/330_section.obj') + + + + diff --git a/test/tutorial_named_section.gpkg b/test/tutorial_named_section.gpkg new file mode 100644 index 0000000..2c4d226 Binary files /dev/null and b/test/tutorial_named_section.gpkg differ diff --git a/viewer_3d/__main__.py b/viewer_3d/__main__.py index d89b260..28c4d1f 100644 --- a/viewer_3d/__main__.py +++ b/viewer_3d/__main__.py @@ -1,6 +1,6 @@ -from PyQt4.QtGui import * -from PyQt4.QtCore import * +from qgis.PyQt.QtGui import * +from qgis.PyQt.QtCore import * import sys from ..project import Project from .viewer_3d import ViewerWindow diff --git a/viewer_3d/camera.py b/viewer_3d/camera.py index 0292da8..b9b1320 100644 --- a/viewer_3d/camera.py +++ b/viewer_3d/camera.py @@ -1,7 +1,8 @@ # -*- coding: UTF-8 -*- -from PyQt4.QtCore import Qt -from PyQt4.QtGui import QVector3D +from builtins import object +from qgis.PyQt.QtCore import Qt +from qgis.PyQt.QtGui import QVector3D class Camera(object): "a simple camera" diff --git a/viewer_3d/scene.py b/viewer_3d/scene.py index aa167d1..d7679b5 100644 --- a/viewer_3d/scene.py +++ b/viewer_3d/scene.py @@ -1,11 +1,13 @@ # -*- coding: UTF-8 -*- +from builtins import zip +from builtins import range import numpy from OpenGL.GL import * from OpenGL.GL import shaders -from PyQt4.QtGui import * -from PyQt4.QtCore import * +from qgis.PyQt.QtGui import * +from qgis.PyQt.QtCore import * from .utility import computeNormals from shapely import wkb @@ -51,6 +53,7 @@ def __init__(self, project, param, texture_binder, parent=None): "edge":None, "section":None, "volume":None, + "volume_section":None, "error":None, "end": None, "inconsistency":None} @@ -59,6 +62,7 @@ def __init__(self, project, param, texture_binder, parent=None): "edge":None, "section":None, "volume":None, + "volume_section":None, "error":None, "end":None} @@ -72,6 +76,7 @@ def __init__(self, project, param, texture_binder, parent=None): "end":None} self.nrml = { "volume":None, + "volume_section":None, "error":None} self.__labels = [] @@ -133,14 +138,6 @@ def compileShaders(self): gl_FragColor.rgb = mix(vec3(0.0), Idiff.xyz, edgeFactor()); gl_FragColor.a = 1. - uTransparency; - //if(any(lessThan(vBC, vec3(0.02)))){ - // gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); - //} - //else{ - // gl_FragColor = Idiff; - //} - //gl_FragColor = vec4(vBC.xyz, 1);//Idiff; - //gl_FragColor = Idiff; } """, GL_FRAGMENT_SHADER) @@ -237,7 +234,6 @@ def rendergl(self, leftv, upv, eye, height, context): glDrawElementsui(GL_LINES, a) glEnable(GL_DEPTH_TEST) - # render volume if self.__param["transparency"] > 0.: glDisable(GL_DEPTH_TEST) @@ -291,6 +287,36 @@ def rendergl(self, leftv, upv, eye, height, context): if self.__useProgram: glUseProgram(0) + layer='volume_section' + if self.__param[layer]: + if self.__param[layer] != self.__old_param[layer]: + self.update(layer) + if self.vtx[layer] is not None and len(self.vtx[layer]): + glEnable(GL_COLOR_MATERIAL) + glDisable(GL_TEXTURE_2D) + glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, [7., 4., 4., 1.]) + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, [7., 4., 4., 1.]) + glColor4f(.7, .4, .4, 1.) + glVertexPointerf(self.vtx[layer]) + glDisableClientState(GL_COLOR_ARRAY) + glDisable(GL_CULL_FACE) + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL) + glNormalPointerf(self.nrml[layer]) + glDrawElementsui(GL_TRIANGLES, self.idx[layer]) + #if not self.__useProgram: + # glDisable(GL_LIGHTING) + # glDisableClientState(GL_COLOR_ARRAY) + # glColor4f(.1, .1, .1, 1.) + # glLineWidth(2) + # glEnable(GL_CULL_FACE) + # glPolygonMode(GL_FRONT,GL_LINE) + # glDrawElementsui(GL_TRIANGLES, self.idx[layer]) + # glPolygonMode(GL_FRONT,GL_FILL) + # glDisable(GL_CULL_FACE) + # glEnableClientState(GL_COLOR_ARRAY) + # glEnable(GL_LIGHTING) + glDisable(GL_COLOR_MATERIAL) + # current section, highlight nodes glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, [1., 1., 0., 1.]) glDisableClientState(GL_COLOR_ARRAY) @@ -450,8 +476,7 @@ def update(self, layer): cur.execute(""" select coalesce(st_collect(n.geom), 'GEOMETRYCOLLECTION EMPTY'::geometry) from albion.section as s - join albion.collar as c on st_intersects(s.geom, c.geom) - join albion.hole as h on h.collar_id=c.id + join albion.hole as h on s.geom && h.geom and st_intersects(s.geom, st_startpoint(h.geom)) join albion.node as n on n.hole_id=h.id where n.graph_id='{}' """.format(self.__param["graph_id"]) @@ -513,6 +538,21 @@ def update(self, layer): self.idx[layer] = numpy.require(numpy.arange(len(self.vtx[layer])).reshape((-1,3)), numpy.int32, 'C') self.nrml[layer] = computeNormals(self.vtx[layer], self.idx[layer]) + elif layer=='volume_section': + cur.execute(""" + select st_collectionhomogenize(coalesce(st_collect(geom), 'GEOMETRYCOLLECTION EMPTY'::geometry)) + from albion.volume_section + where graph_id='{}' + """.format(self.__param["graph_id"])) + geom = wkb.loads(bytes.fromhex(cur.fetchone()[0])) + self.vtx[layer] = numpy.require(numpy.array([tri.exterior.coords[:-1] for tri in geom]).reshape((-1,3)), numpy.float32, 'C') + if len(self.vtx[layer]): + self.vtx[layer] += self.__offset + self.vtx[layer][:,2] *= self.__param["z_scale"] + self.idx[layer] = numpy.require(numpy.arange(len(self.vtx[layer])).reshape((-1,3)), numpy.int32, 'C') + self.nrml[layer] = computeNormals(self.vtx[layer], self.idx[layer]) + + elif layer=='error': cur.execute(""" select st_collectionhomogenize(coalesce(st_collect(triangulation), 'GEOMETRYCOLLECTION EMPTY'::geometry)) @@ -531,7 +571,7 @@ def update(self, layer): self.__old_param[layer] = self.__param[layer] def setGraph(self, graph_id): - for layer in ['node', 'edge', 'volume', 'section', 'error', 'end']: + for layer in ['node', 'edge', 'volume', 'volume_section', 'section', 'error', 'end']: if self.__param[layer] != self.__old_param[layer]: self.update(layer) self.__old_param["graph_id"] = graph_id @@ -540,10 +580,10 @@ def setGraph(self, graph_id): def setZscale(self, scale): factor = float(scale)/self.__old_param["z_scale"] - for layer in ['node', 'edge', 'volume', 'section', 'error', 'end']: + for layer in ['node', 'edge', 'volume', 'volume_section', 'section', 'error', 'end']: if self.vtx[layer] is not None: self.vtx[layer][:,2] *= factor - if layer in ['volume', 'error']: + if layer in ['volume', 'volume_section', 'error']: self.nrml[layer] = computeNormals(self.vtx[layer], self.idx[layer]) for scatter in self.__labels: diff --git a/viewer_3d/utility.py b/viewer_3d/utility.py index afca416..22826a5 100644 --- a/viewer_3d/utility.py +++ b/viewer_3d/utility.py @@ -1,5 +1,6 @@ # -*- coding: UTF-8 -*- +from builtins import str import sys import re import numpy diff --git a/viewer_3d/viewer_3d.py b/viewer_3d/viewer_3d.py index b43430a..590aac6 100644 --- a/viewer_3d/viewer_3d.py +++ b/viewer_3d/viewer_3d.py @@ -8,10 +8,11 @@ from OpenGL import GLU -from PyQt4.QtOpenGL import QGLWidget, QGLPixelBuffer, QGLFormat -from PyQt4.QtCore import * -from PyQt4.QtGui import * -from PyQt4 import uic +from PyQt5.QtOpenGL import QGLWidget, QGLFormat +from qgis.PyQt.QtCore import Qt +from qgis.PyQt.QtGui import QVector3D +from qgis.PyQt.QtWidgets import QMessageBox, QMainWindow +from qgis.PyQt import uic import os import re @@ -33,6 +34,7 @@ def __init__(self, project=None, parent=None): "end": False, "edge": False, "volume": False, + "volume_section": True, "error": False, "section": False, "z_scale": 1, @@ -51,14 +53,14 @@ def __init__(self, project=None, parent=None): self.previous_pick = None def refresh_data(self): - if self.scene and self.__project.has_collar: + if self.scene and self.__project.has_hole: self.resetScene(self.__project, False) - for layer in ['node', 'edge', 'volume', 'section', 'error', 'end']: + for layer in ['node', 'edge', 'volume', 'volume_section', 'section', 'error', 'end']: self.scene.update(layer) self.update() def resetScene(self, project, resetCamera=True): - if project and project.has_collar: + if project and project.has_hole: self.scene = Scene(project, self.__param, self.bindTexture, self) if resetCamera: at = self.scene.center @@ -67,7 +69,7 @@ def resetScene(self, project, resetCamera=True): self.camera = Camera(eye, at) if self.__param['graph_id']: - for layer in ['node', 'edge', 'volume', 'section', 'error', 'end']: + for layer in ['node', 'edge', 'volume', 'volume_section', 'section', 'error', 'end']: self.scene.update(layer) self.update() else: @@ -112,6 +114,11 @@ def toggle_volumes(self, state): self.__param["volume"] = state self.update() + def toggle_volumes_section(self, state): + self.__param["volume_section"] = state + self.update() + + def toggle_errors(self, state): self.__param["error"] = state self.update() diff --git a/viewer_3d/viewer_controls.py b/viewer_3d/viewer_controls.py index f144baf..8e1a267 100644 --- a/viewer_3d/viewer_controls.py +++ b/viewer_3d/viewer_controls.py @@ -1,10 +1,7 @@ # coding=UTF-8 import os -#from qgis.core import * -#from qgis.gui import * -from PyQt4.QtCore import * -from PyQt4.QtGui import * -from PyQt4 import uic +from qgis.PyQt.QtWidgets import QWidget, QMenu +from qgis.PyQt import uic from functools import partial class ViewerControls(QWidget): @@ -24,6 +21,7 @@ def __init__(self, viewer, iface=None, parent=None): ('edges', self.__viewer.toggle_edges, True), ('ends', self.__viewer.toggle_ends, True), ('volumes', self.__viewer.toggle_volumes, False), + ('volumes sections', self.__viewer.toggle_volumes_section, True), ('errors', self.__viewer.toggle_errors, False), ]: a = menu.addAction(l)