11import $ from 'jquery' ;
22import { AJAX } from '../modules/ajax.ts' ;
33import { escapeHtml } from '../modules/functions/escape.ts' ;
4- import { Feature , Map , View } from 'ol' ;
5- import { Attribution , MousePosition , Zoom } from 'ol/control' ;
4+ import Feature from 'ol/Feature' ;
5+ import Map from 'ol/Map' ;
6+ import View from 'ol/View' ;
7+ import Attribution from 'ol/control/Attribution' ;
8+ import MousePosition from 'ol/control/MousePosition' ;
9+ import Zoom from 'ol/control/Zoom' ;
610import { createStringXY } from 'ol/coordinate' ;
711import { isEmpty } from 'ol/extent' ;
8- import { LineString , MultiLineString , MultiPoint , MultiPolygon , Point , Polygon } from 'ol/geom' ;
9- import { Tile , Vector as VectorLayer } from 'ol/layer' ;
12+ import WKT from 'ol/format/WKT' ;
13+ import Geometry from 'ol/geom/Geometry' ;
14+ import GeometryCollection from 'ol/geom/GeometryCollection' ;
15+ import Tile from 'ol/layer/Tile' ;
16+ import VectorLayer from 'ol/layer/Vector' ;
17+ import OSM from 'ol/source/OSM' ;
18+ import VectorSource from 'ol/source/Vector' ;
19+ import Circle from 'ol/style/Circle' ;
20+ import Fill from 'ol/style/Fill' ;
21+ import Stroke from 'ol/style/Stroke' ;
22+ import Style from 'ol/style/Style' ;
23+ import Text from 'ol/style/Text' ;
1024import { get as getProjection } from 'ol/proj' ;
11- import { OSM , Vector as VectorSource } from 'ol/source' ;
12- import { Circle , Fill , Stroke , Style , Text } from 'ol/style' ;
1325
1426/**
1527 * @fileoverview functions used for visualizing GIS data
@@ -422,20 +434,72 @@ class SvgVisualization extends GisVisualization {
422434 }
423435}
424436
437+ interface OlGeometryData {
438+ wkt : string ;
439+ srid ?: number ;
440+ label ?: string ;
441+ }
442+
443+ export interface OlData {
444+ geometries : OlGeometryData [ ] ;
445+ colors : [ number , number , number ] [ ] ;
446+ }
447+
425448class OlVisualization extends GisVisualization {
426449 private olMap : any = undefined ;
427450
428- private data : any [ ] ;
451+ private data : OlData ;
429452
430- /**
431- * @param {function(HTMLElement): ol.Map } initFn
432- */
433- constructor ( target : HTMLElement , data : any [ ] ) {
453+ constructor ( target : HTMLElement , data : OlData ) {
434454 super ( target ) ;
435455
436456 this . data = data ;
437457 }
438458
459+ getFeaturesFromData ( geometries : OlGeometryData [ ] ) : Feature [ ] {
460+ let features : Feature [ ] = [ ] ;
461+ const addFeature = ( index : number , data : OlGeometryData , geometry : Geometry ) => {
462+ const feature = new Feature ( geometry ) ;
463+ feature . set ( 'index' , index , true ) ;
464+ if ( data . label ) {
465+ feature . set ( 'label' , data . label , true ) ;
466+ }
467+
468+ features . push ( feature ) ;
469+ } ;
470+
471+ const projections = { } ;
472+ const reader = new WKT ( ) ;
473+ for ( let i = 0 , ii = geometries . length ; i < ii ; ++ i ) {
474+ const data = geometries [ i ] ;
475+ const dataProjection = 'EPSG:' + ( data . srid ?? 4326 ) ;
476+ if ( ! ( dataProjection in projections ) ) {
477+ projections [ dataProjection ] = getProjection ( dataProjection ) ;
478+ }
479+
480+ // Skip features which use an unknown projection
481+ if ( ! projections [ dataProjection ] ) {
482+ continue ;
483+ }
484+
485+ const geometry = reader . readGeometry ( data . wkt , {
486+ dataProjection,
487+ featureProjection : 'EPSG:3857' ,
488+ } ) ;
489+
490+ if ( geometry . getType ( ) === 'GeometryCollection' ) {
491+ const geometries = ( geometry as GeometryCollection ) . getGeometriesArrayRecursive ( ) ;
492+ geometries . forEach ( ( geometry ) => {
493+ addFeature ( i , data , geometry ) ;
494+ } ) ;
495+ } else {
496+ addFeature ( i , data , geometry ) ;
497+ }
498+ }
499+
500+ return features ;
501+ }
502+
439503 drawOpenLayers ( ) {
440504 if ( ! document . querySelector ( 'script[src*="js/vendor/openlayers/openlayers.js"]' ) ) {
441505 return undefined ;
@@ -450,25 +514,86 @@ class OlVisualization extends GisVisualization {
450514 document . head . appendChild ( link ) ;
451515 }
452516
517+ const styleFactory = {
518+ Point : ( color : [ number , number , number ] ) => new Style ( {
519+ image : new Circle ( {
520+ radius : 3 ,
521+ fill : new Fill ( { color : 'white' } ) ,
522+ stroke : new Stroke ( { width : 2 , color } ) ,
523+ } ) ,
524+ text : new Text ( {
525+ stroke : new Stroke ( { width : 2 , color : 'white' } ) ,
526+ } ) ,
527+ } ) ,
528+ LineString : ( color : [ number , number , number ] ) => new Style ( {
529+ stroke : new Stroke ( { width : 2 , color : color } ) ,
530+ text : new Text ( {
531+ stroke : new Stroke ( { width : 2 , color : 'white' } ) ,
532+ } ) ,
533+ } ) ,
534+ Polygon : ( color : [ number , number , number ] ) => new Style ( {
535+ fill : new Fill ( { color : [ ...color , 0.8 ] } ) ,
536+ stroke : new Stroke ( { width : .5 , color : 'black' } ) ,
537+ text : new Text ( {
538+ stroke : new Stroke ( { width : 2 , color : 'white' } ) ,
539+ } ) ,
540+ } ) ,
541+ } ;
542+
453543 const vectorSource = new VectorSource ( {
454- features : getFeaturesFromOpenLayersData ( this . data ) ,
544+ features : this . getFeaturesFromData ( this . data . geometries ) ,
545+ } ) ;
546+
547+ let currentFeatureIndex : number | undefined ;
548+ const colors = this . data . colors ;
549+ const cache : Record < string , Style > = { } ;
550+ const vectorLayer = new VectorLayer ( {
551+ source : vectorSource ,
552+ style : function ( feature ) {
553+ const index = feature . get ( 'index' ) ;
554+ const type = feature . getGeometry ( ) . getType ( ) . replace ( 'Multi' , '' ) ;
555+ const selected = index === currentFeatureIndex ;
556+ const key = type + ( selected ? '+' : '-' ) + ( index % colors . length ) ;
557+
558+ let style = cache [ key ] ;
559+ if ( ! style ) {
560+ let color = colors [ index % colors . length ] ;
561+ if ( selected ) {
562+ color = color . map ( ( c ) => c * 1.2 ) as [ number , number , number ] ;
563+ }
564+
565+ style = styleFactory [ type ] ( color ) ;
566+ cache [ key ] = style ;
567+ }
568+
569+ style . getText ( ) . setText ( feature . get ( 'label' ) ) ;
570+
571+ return style ;
572+ } ,
455573 } ) ;
456574 const map = new Map ( {
457575 target : this . target ,
458576 layers : [
459577 new Tile ( { source : new OSM ( ) } ) ,
460- new VectorLayer ( { source : vectorSource } ) ,
578+ vectorLayer ,
461579 ] ,
462580 view : new View ( { center : [ 0 , 0 ] , zoom : 4 } ) ,
463581 controls : [
464582 new MousePosition ( {
465583 coordinateFormat : createStringXY ( 4 ) ,
466584 projection : 'EPSG:4326'
467585 } ) ,
468- new Zoom ,
469- new Attribution
586+ new Zoom ( ) ,
587+ new Attribution ( ) ,
470588 ]
471589 } ) ;
590+ map . on ( 'pointermove' , ( evt ) => {
591+ const newFeatureIndex = ( map . forEachFeatureAtPixel ( evt . pixel , ( f ) => f ) ) ?. get ( 'index' ) as number ;
592+ if ( newFeatureIndex !== currentFeatureIndex ) {
593+ currentFeatureIndex = newFeatureIndex ;
594+ vectorLayer . changed ( ) ;
595+ }
596+ } ) ;
472597
473598 const extent = vectorSource . getExtent ( ) ;
474599 if ( ! isEmpty ( extent ) ) {
@@ -499,83 +624,16 @@ class OlVisualization extends GisVisualization {
499624 }
500625}
501626
502- function getFeaturesFromOpenLayersData ( geometries : any [ ] ) : any [ ] {
503- let features = [ ] ;
504- for ( const geometry of geometries ) {
505- if ( geometry . isCollection ) {
506- features = features . concat ( getFeaturesFromOpenLayersData ( geometry . geometries ) ) ;
507-
508- continue ;
509- }
510-
511- let olGeometry : any = null ;
512- const style : any = { } ;
513- if ( geometry . geometry . type === 'LineString' ) {
514- olGeometry = new LineString ( geometry . geometry . coordinates ) ;
515- style . stroke = new Stroke ( geometry . style . stroke ) ;
516- } else if ( geometry . geometry . type === 'MultiLineString' ) {
517- olGeometry = new MultiLineString ( geometry . geometry . coordinates ) ;
518- style . stroke = new Stroke ( geometry . style . stroke ) ;
519- } else if ( geometry . geometry . type === 'MultiPoint' ) {
520- olGeometry = new MultiPoint ( geometry . geometry . coordinates ) ;
521- style . image = new Circle ( {
522- fill : new Fill ( geometry . style . circle . fill ) ,
523- stroke : new Stroke ( geometry . style . circle . stroke ) ,
524- radius : geometry . style . circle . radius ,
525- } ) ;
526- } else if ( geometry . geometry . type === 'MultiPolygon' ) {
527- olGeometry = new MultiPolygon ( geometry . geometry . coordinates ) ;
528- style . fill = new Fill ( geometry . style . fill ) ;
529- style . stroke = new Stroke ( geometry . style . stroke ) ;
530- } else if ( geometry . geometry . type === 'Point' ) {
531- olGeometry = new Point ( geometry . geometry . coordinates ) ;
532- style . image = new Circle ( {
533- fill : new Fill ( geometry . style . circle . fill ) ,
534- stroke : new Stroke ( geometry . style . circle . stroke ) ,
535- radius : geometry . style . circle . radius ,
536- } ) ;
537- } else if ( geometry . geometry . type === 'Polygon' ) {
538- olGeometry = new Polygon ( geometry . geometry . coordinates ) ;
539- style . fill = new Fill ( geometry . style . fill ) ;
540- style . stroke = new Stroke ( geometry . style . stroke ) ;
541- } else {
542- throw new Error ( ) ;
543- }
544-
545- if ( geometry . geometry . srid !== 3857 ) {
546- const source = 'EPSG:' + ( geometry . geometry . srid !== 0 ? geometry . geometry . srid : 4326 ) ;
547- const sourceProj = getProjection ( source ) ;
548-
549- if ( sourceProj ) {
550- olGeometry = olGeometry . transform (
551- source ,
552- 'EPSG:3857'
553- ) ;
554- }
555- }
556-
557- if ( geometry . style . text ) {
558- style . text = new Text ( geometry . style . text ) ;
559- }
560-
561- const feature = new Feature ( olGeometry ) ;
562- feature . setStyle ( new Style ( style ) ) ;
563- features . push ( feature ) ;
564- }
565-
566- return features ;
567- }
568-
569627class GisVisualizationController {
570628 private svgVis : SvgVisualization | undefined = undefined ;
571629
572630 private olVis : OlVisualization | undefined = undefined ;
573631
574632 private boundOnChoiceChange : any ;
575633
576- private olData : any [ ] ;
634+ private olData : OlData ;
577635
578- constructor ( olData : any [ ] ) {
636+ constructor ( olData : OlData ) {
579637 this . boundOnChoiceChange = this . onChoiceChange . bind ( this ) ;
580638 this . olData = olData ;
581639
0 commit comments