[GIS] Adding offset to feature of layer in OpenLayers 4.6

angularopenlayers

I have GeoJSON vectors on OpenLayers map where I have OlRegularShape 'square' points in my layer as shown below.

enter image description here

I want to add another square point to show different stats as shown below.

enter image description here

Second square does not has any fixed coordinates it should be bounded to the big square whenever map zoomed in and zoomed out small square should move with the big one.

I tried several ways to move the small square but neither works as expected. Can anyone suggest me a solution to overcome this problem?

Is there any way to set offset to features rather than layer?

Is there any easy way to accomplish this feature on client ?

JS code for layer creation

let existsZoomLevel = this.settingsService.getLiveMapZoomLevel()
let mapZoomLevel = existsZoomLevel ? Number(existsZoomLevel) : 16;

this.source = new OlVector({});
// big square layer
this.dockLayer = new OlVectorLayer({
  style: (f, r) => this.styleDockFunction(f, r),
  source: new OlVector({}),
});

//small square layer
this.countLayer = new OlVectorLayer({
  style: (f, r) => this.styleLabelFunction(f, r),
  source: new OlVector({})
});





let tileLayer = new OlTileLayer({
  source: new OlXYZ({
    crossOrigin: null,
    url: 'https://cartodb-basemaps-{a-c}.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png'
  })
});


let view = new OlView({
  center: OlProj.fromLonLat([5.73271, 58.96564]),
  zoom: mapZoomLevel,
});

this.map = new OlMap({
  layers: [tileLayer, this.dockLayer, this.countLayer],
  view: view
});

Style function for layers below

//big square style
static getDockingStationsStyles(highlighted: boolean = false, color: string = '#FF2C00'): OlStyle[] {
let featureColor = highlighted ? '#FF2C00' : '#04A3E3';
if (color) {
  featureColor = color;
}
let imageRadius = highlighted ? 5 : 2;
let strokeWidth = highlighted ? 3 : 2;

let styles = [
  // polygon
  new OlStyle({
    stroke: new OlStroke({
      color: featureColor,
      width: strokeWidth
    })
  }),
  // point
  // new OlStyle({
  //   image: new OlCircle({
  //     radius: imageRadius,
  //     fill: new OlFill({
  //       color: featureColor
  //     }),
  //   }),
  //   text: new OlText({
  //     font: `14px Roboto, "Helvetica Neue", sans-serif`,
  //     offsetX: 10,
  //     offsetY: 0,
  //     textAlign: 'left',
  //     placement: 'point',
  //     fill: new OlFill({
  //       color: '#000'
  //     }),
  //     stroke: new OlStroke({
  //       color: '#FFF',
  //       width: 2
  //     })
  //   }),
  //   geometry: function (feature) {
  //     let data = feature.get('data');
  //     let coordinates = [data.Position.Longitude, data.Position.Latitude];
  //     return new OlPoint(OlProj.fromLonLat(coordinates));
  //   }
  // })
  new OlStyle({
    image: new OlRegularShape({
      radius: 20,
      fill: new OlFill({
        color: featureColor
      }),
      points: 4,
      angle: Math.PI / 4
    }),
    text: new OlText({
      font: `14px Roboto, "Helvetica Neue", sans-serif`,
      offsetX: 0,
      offsetY: 0,
      textAlign: 'center',
      placement: 'point',
      fill: new OlFill({
        color: 'white'
      }),
      // stroke: new OlStroke({
      //   color: '#FFF',
      //   width: 2
      // })
    }),
    // geometry: function (feature) {
    //   let data = feature.get('data');
    //   let coordinates = [data.Position.Longitude, data.Position.Latitude];
    //   return new OlPoint(OlProj.fromLonLat(coordinates));
    // }
  })
]
return styles;


}

// small square style
  static getTickStationsStyles(highlighted: boolean = false, color: string = '#FF2C00'): OlStyle[] {

let featureColor = highlighted ? '#FF2C00' : '#04A3E3';
if (color) {
  featureColor = color;
}
let imageRadius = highlighted ? 5 : 2;
let strokeWidth = highlighted ? 3 : 2;

let styles = [
  // polygon
  new OlStyle({
    stroke: new OlStroke({
      color: featureColor,
      width: strokeWidth
    })
  }),
  // point
  new OlStyle({
    image: new OlRegularShape({
      radius: 10,
      fill: new OlFill({
        color: featureColor
      }),
      points: 4,
      angle: Math.PI / 4
    }),
    text: new OlText({
      font: `14px Roboto, "Helvetica Neue", sans-serif`,
      offsetX: 0,
      offsetY: 0,
      textAlign: 'center',
      placement: 'point',
      fill: new OlFill({
        color: '#000'
      }),
      stroke: new OlStroke({
        color: '#FFF',
        width: 2
      })
    }),
    // geometry: function (feature) {
    //   let data = feature.get('data');
    //   let coordinates = [data.Position.Longitude, data.Position.Latitude];
    //   return new OlPoint(OlProj.fromLonLat(coordinates));
    // }
  })
]
return styles;


}

feature adding for two squares

getDockingStationAreaFeature(station: DockingStation) {

    let point = [station.Position.Longitude, station.Position.Latitude];
    let polygon = new OlPoint(point);
    polygon.transform('EPSG:4326', 'EPSG:3857');
    station.StationStatus = this.getDockedBikeStatus(station.DockingStationId);
    let feature = new OlFeature({
      name: station.NumberOfAvailableBikes.toString(),
      geometry: polygon,
      data: station,
      type: "dock"
    });

    feature.setId(station.DockingStationId);
    return feature;
  }

  getTickStationAreaFeature(station: DockingStation) {
    let point = [station.Position.Longitude, station.Position.Latitude];
    let feature = new OlFeature({
      name: station.TotalNumberOfPoints.toString(),
      geometry: new OlPoint(OlProj.transform(point, 'EPSG:4326', 'EPSG:3857')),
      data: station,
      type: "tick"
    });

    feature.setId(station.DockingStationId);
    return feature;
  }

  getDockedBikeStatus(dockingStationId) {
    let filteredBikes = this.filteredBikes.filter(x => x.DockingStationId == dockingStationId);
    let status = StationColors.AVAILABLE_BIKES;
    for (let bike in filteredBikes) {
      if (filteredBikes[bike]["Bike"].Disabled) {
        status = StationColors.AVAILABLE_WITH_DISABLED;
        break;
      }
      else if (!filteredBikes[bike]["Bike"].Disabled && filteredBikes[bike]["Bike"].Acknowledged != 0) {
        status = StationColors.AVAILABLE_WITH_ERRORS;
        break;
      }
    }
    return status;
  }

  public addDockingStation(station: DockingStation, centerToPoint: boolean = true): void {

    let point = [station.Position.Longitude, station.Position.Latitude];
    let projectedPoint = OlProj.fromLonLat(point);
    station.AddressStr = DockingStationExtension.GetAddress(station.Address);
    let areaFeature = this.getDockingStationAreaFeature(station);
    let tickFeature = this.getTickStationAreaFeature(station);
    let dockSource = this.dockLayer.getSource();
    let tickSource = this.countLayer.getSource();

    dockSource.addFeature(areaFeature);
    //tickFeature.getGeometry().translate(station.Position.Longitude + 1, station.Position.Longitude + 0.0004);
    tickSource.addFeature(tickFeature);
    if (centerToPoint) {
      this.map.getView().setCenter(projectedPoint);
    }
  }

Best Answer

As I wrote in my comment, I'm not familiar with Angular environment. Since you replied that any hint is welcome, here it is, with standard JS.

The simplest way to create such markers is with ol.Overlay layer, using HTML elements for marker design/display.

First you create hidden dummy div with two child div elements for marker:

<div id="map" class="map"></div>
<div style="display: none;">
  <div id="marker">
     <div id="marker-txt" class="marker-txt"></div>
     <div id="marker-txt2" class="marker-txt2">x</div>
  </div>
</div>

Here marker-txt is for main marker text and marker-txt2 for smaller text in upper right corner.

Then you define appropriate CSS classes for marker display (this takes a bit of experimenting):

.marker-txt {
  font-family: Verdana, Geneva, Tahoma, sans-serif;
  width: 35px;
  height: 35px;
  background-color: #333F48;
  border-radius: 4px;
  color: white;
  display: flex;
  justify-content: center;
  align-items: center;
}
.marker-txt2 {
  position: absolute;
  right: -9px;
  top: -9px;
  font-family: Verdana, Geneva, Tahoma, sans-serif;
  font-size: 7pt;
  width: 16px;
  height: 16px;
  background-color: red;
  color: white;
  display: flex;
  justify-content: center;
  align-items: center;
}

Then you define a function for creating desired markers, using ol.Overlay. Since it will be used for several markers, .cloneNode method is used to clone dummy marker for each newly created marker:

function createMarker(pos, txt, txt2, color2) {
  var elm = document.getElementById('marker').cloneNode(true);
  var elm2 = elm.querySelector("#marker-txt2");
  elm.querySelector("#marker-txt").innerHTML = txt;
  elm2.innerHTML = txt2;
  elm2.style.backgroundColor = color2;
  var marker = new ol.Overlay({
    position: pos,
    positioning: 'center-center',
    element: elm
  });
  map.addOverlay(marker);
} 

Here is example of creation of three markers:

var pos1 = ol.proj.fromLonLat([16.3725, 48.208889]);
var pos2 = ol.proj.fromLonLat([16, 48]);
var pos3 = ol.proj.fromLonLat([16, 48.4]);

createMarker(pos1, '1', '5+', 'green');
createMarker(pos2, '2', '2-', 'red');
createMarker(pos3, '3', '2+', 'orange');

And that's how they look like when displayed:

enter image description here

Related Question