In OpenLayers 2 it was possible to extend the style definition with dynamic parts - special functions that calculates a specific style value at render time. Is there an equivalent in OpenLayers 3?
Here sample code from OpenLayers 2:
var stdStyleMap = new OpenLayers.StyleMap({
"default": new OpenLayers.Style({
/* fixed value */
fillOpacity: 0.8,
/* value from server response */
fillColor: "${fillcolor}",
/* value calculated at render time */
pointRadius: "${getPointRadius}",
}, {
context: {
/* function that calculates the point radius */
getPointRadius: function(feature) {
if (feature.attributes && feature.attributes.pointRadius)
return feature.attributes.pointRadius;
else
return 5;
}
}})
});
Here's a good example of using custom styles for polygons from the Openlayers site.
But the following is an example that answers a question i posted... so, yay for both of us... maybe.
// we'd normally pass feature & resolution parameters to the function, but we're going to
// make this dynamic, so we'll return a style function for later use which will take those params.
DynamicStyleFunction = ( function( /* no feat/res yet!*/ ) {
/**
you really only get style are rendered upon simple geometries, not features. features are made of different geometry types, and styleFunctions are passed a feature that has its geometries rendered. in terms of styling vector geometries, you have only a few options. side note: if there's some feature you expect to see on the the map and it's not showing up, you probably haven't properly styled it. Or, maybe it hasn't been put it in a collection that is included in the source layer... which is a hiccup for a different day.
*/
// for any geometry that you want to be rendered, you'll want a style.
var styles = {};
var s = styles;
/**
an ol.layer.Vector or FeatureOverlay, renders those features in its source by applying Styles made of Strokes, Fills, and Images (made of strokes and fills) on top of the simple geometries which make up the features
Stroke styles get applied to ol.geom.GeometryType.LINE_STRING
MULTI_LINE_STRING can get different styling if you want
*/
var strokeLinesWhite = new ol.style.Stroke({
color: [255, 255, 255, 1], // white
width: 5,
})
var whiteLineStyle new ol.style.Style({
stroke: strokeLinesWhite
})
styles[ol.geom.GeometryType.LINE_STRING] = whiteLineStyle
/**
Polygon styles get applied to ol.geom.GeometryType.POLYGON
Polygons are gonna get filled. They also have Lines... so they can take stroke
*/
var fillPolygonBlue = new ol.style.Style({
fill: new ol.style.Fill({
color: [0, 153, 255, 1], // blue
})
})
var whiteOutlinedBluePolygon = new ol.style.Style({
stroke: strokeLinesWhite,
fill: fillPolygonBlue,
})
styles[ol.geom.GeometryType.POLYGON] = fillPolygonBlue
/**
Circle styles get applied to ol.geom.GeometryType.POINT
They're made with a radius and a fill, and the edge gets stroked...
*/
var smallRedCircleStyle = new ol.style.Style({
image: new ol.style.Circle({
radius: 5,
fill: new ol.style.Fill({
color: '#FF0000', // red... but i had to look it up
})
})
})
var whiteBigCircleWithBlueBorderStyle = new ol.style.Style({
image: new ol.style.Circle({
radius: 10,
fill: new ol.style.Fill({
color: '#FFFFFF' // i guessed it
})
}),
stroke: new.ol.style.Stroke({
color: '#0000FF', // blue
width: 5
})
})
// render all points as small red circles
styles[ol.geom.GeometryType.POINT] = smallRedCircleStyle
// if you pass an array as the style argument, every rendering of the feature will apply every defined style style rendered with the geometry as the argument. that can be a whole lot of rendering in a FeatureOverlay...
smallRedCircleStyle.setZIndex(Infinity)
whiteBigCircleWithBlueBorderStyle.setZIndex(Infinity -1) // that prob wouldn't work, but i hope it's instructive that you can tinker with styles
// so...
var bullseyePointStyle = [ smallRedCircleStyle, whiteBigCircleWithBlueBorderStyle ];
return function dynamicStyleFunction (feature, resolution){
// this is the actual function getting invoked on each function call
// do whatever you want with the feature/resolution.
if (Array.indexOf(feature.getKeys('thisIsOurBullseyeNode') > -1) {
return bullseyePointStyle
} else if (feature.getGeometryName('whiteBlueBox')){
return whiteOutlinedBluePolygon
} else {
return styles[feature.getGeometryName()]
}
}
})()
Yes there is style function that takes a feature and the current resolution, see some examples in the workshop: http://openlayers.org/workshop/vector/style.html
Related
So I've built a map with several different colored lines. I chopped up the lines into a string of segments at each dot.
What I'm trying to do is use javascript to change the colors of the lines and eventually move to being able to change the dynamically with a feed.
I made a color chart that I'm trying to get the line segment object to find its color and change to the proper corresponding color. The dots pulsate the fill color with greensock properly but the lines are giving me trouble. I keep getting an object isn't a function error.
I've tried a few different ways to set up the formula but I'm stuck.
var colorSet = {
'#ff0000': '#ff6666',
'#008250': '#42a680',
'#a3238f': '#bd73b2',
'#0079c2': '#42a0db',
'#ff8c00': '#ffba66',
'#96c800': '#cae087',
'#a86000': '#c28f4e',
'#999999': '#cccccc',
'#ffe000': '#fff399'
}
var animateThis = function(obj) {
var getStroke = obj.getAttribute('stroke');
TweenLite.to(obj, 1.5, {
fill: "#bbbbbb",
yoyo: true,
repeat: -1
});
};
Here is my fiddle: https://jsfiddle.net/Spiderian/hzafm6vk/3/#&togetherjs=6e0qwPovaP
To color lines you need to use stroke, not fill. And on <line> not on <g>.
getStroke is not used in you code. (And there is no getAttribute on $ by the way. There is attr())
colorSet also unused, and I don't really understand what is it for. As all of lines and points in the jsfiddle you've provided are of the same color.
Don't really understand why there are two lines for each <g>.
But nevertheless here is what you need to do:
// change all line selectors from this
// const lsSIT0001 = $("#SIT0001");
// const lsSIT0002 = $("#SIT0002");
// ...
// to this
const lsSIT0001 = $("#SIT0001 line");
const lsSIT0002 = $("#SIT0002 line");
// ...
// because you need to change stroke color of `<line>`s, not `<g>`s
// split
// var itemsToAnimate = [ lsSIT0020, ..., lsSIT0001, SIR4947, ..., SIR4629 ]
// into
const linesToAnimate = [ lsSIT0020, ..., lsSIT0001 ]
const pointsToAnimate = [ SIR4947 , ..., SIR4629 ]
// run separate animations for lines and dots
const animatePoints = obj => TweenLite.to(obj, 1.5, { fill : "#ff0000", yoyo: true, repeat: -1 })
const animateLines = obj => TweenLite.to(obj, 1.5, { stroke: "#0000ff", yoyo: true, repeat: -1 })
linesToAnimate .forEach(animateLines )
pointsToAnimate.forEach(animatePoints)
Update:
To use colorSet you need to either
set style='stroke: some_color' on each <line> and then use it like this:
const style = obj.eq(i).attr('style')
// `i` is the index of a line inside <g> the color of which you want to get
TweenLite.to(obj, 1.5, { stroke: colorSet[style], ... })
In this case colorSet should look like
const colorSet = {
"stroke: #ff0000": '#ff6666',
...
"stroke: #ffe000": '#fff399',
}
or get computed styles with obj.get(i).computedStyleMap().get("stroke").toString()
In this case colorSet should look like
const colorSet = {
"rgb(255, 0, 0)": '#ff6666',
...
"rgb(255, 224, 0)": '#fff399',
}
But it's experimental technology. So probably don't :)
Or better yet - make a map not from color to color but from ids to color:
const colorSet = {
"#SIT0001": '#ff6666',
...
"#SIT0020": '#fff399',
}
Then you won't need to get colors from DOM.
Background
Specs
OpenLayers 4.4.1
OSM
I'm fairly new to OpenLayers and have never used vectors before (primarily because I found out that I was using OpenLayers version 1, and had to relearn everything).
My application adds circles to a map relating to a position with a specific radius indicating position accuracy.
In its operation, multiple circles are added to the map at different times.
This is my code for loading the map:
var map = new ol.Map({
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
})
],
target: 'mapdiv',
controls: ol.control.defaults({
attributionOptions: /** #type {olx.control.AttributionOptions} */ ({
collapsible: false
})
}),
view: new ol.View({
//center: [0, 0],
zoom: 16
})
});
//this is where all map 'features' (circles) are stored
var vectorSource = new ol.source.Vector({
projection: 'EPSG:4326'
});
As you can see, I load the 'vector source' right after the map as I understood that it holds all 'vectors' which are displayed on the map so long as you specify it as the 'source'.
This is the code I use to generate the circle (source) (I tweaked it at getPointResolution because the OP made a mistake):
//code from https://stackoverflow.com/a/28299599
function addCircle(map, vectorSource, radius) {
var view = map.getView();
var projection = view.getProjection();
var resolutionAtEquator = view.getResolution();
var center = view.getCenter();
var pointResolution = ol.proj.getPointResolution(projection, resolutionAtEquator, center);
var resolutionFactor = resolutionAtEquator/pointResolution;
var radius = (radius / ol.proj.METERS_PER_UNIT.m) * resolutionFactor;
var circle = new ol.geom.Circle(center, radius);
var circleFeature = new ol.Feature(circle);
// vector layer
vectorSource.addFeature(circleFeature);
var vectorLayer = new ol.layer.Vector({
source: vectorSource
});
map.addLayer(vectorLayer);
}
Problem
Loading one circle goes normally, adds a blue stroked, opaque circle at the specified location with specified radius.
Loading a second circle appears more opaque than the last. Moving the map to the previous circle, it is also more opaque.
With each added circle, the apparent opacity increases for all displayed circles.
Running vectorLayer.getOpacity() in every circle generation results in 1, when clearly the circle is translucent, becoming increasingly opaque with every new circle.
Summary
Looking around, it appears that often it is the case that the developer is reloading the same circle over and over until many are stacked on top of one another. It almost seems like this is the case for me too, except I've triple-checked that I'm only running addCircle() once and the circle is in a different position than the last.
Is it possible that OpenLayers is redrawing all previous circles with every new circle?
Maybe this isn't related to getOpacity but has to do with the color as an rgba() combination...
Question
I want every circle to remain the same after drawing new circles. The default opacity and color is fine.
Am I doing something wrong?
Here's a fiddle as an example - https://jsfiddle.net/f5zrLt20/5/
Define the layer when defining the vectorSource:
var layer = null;
//this is where all map 'features' (circles) are stored
var vectorSource = new ol.source.Vector({
projection: 'EPSG:4326'
});
And check if it exists on creating a new circle:
// If layer is not yet set, create new layer and add it to map
if (!layer) {
vectorSource.addFeature(circleFeature);
layer = new ol.layer.Vector({
source: vectorSource
});
map.addLayer(layer);
}
//Otherwise, just add feature to the source
else {
layer.getSource().addFeature(circleFeature);
}
I'd like to be able to tell what ol.layer.Group(s) a layer is part of during a user interaction, without going through all the groups on the map top-down.
Is there a way to do this? I'm currently using ol3 v3.10.2.
During initialising of your layers asign an attribute, on each layer, to verify the group this layer belongs to. like so:
var vector = new ol.layer.Vector({
GROUP : 'group1',
source: vectorSource,
style: new ol.style.Style({
image: new ol.style.Circle({
radius: 5,
fill: new ol.style.Fill({color: '#FFFFFF'}),
stroke: new ol.style.Stroke({
color: '#000000',
width: 3
})
})
})
});
and then you may get the group like so:
layer.get('GROUP');
Looking at the ol.layer.Group, ol.collection and goog.array, none of these set an backwards reference to the layer. So you'll have to dig down trough all the groups, as far as I can see.
I have several shape definitions stored in files, i.e.
Kinetic.Rect({width : 150,height : 50,stroke : 'black',fill :'#00D2FF',strokeWidth : .5,cornerRadius : 25,name : 'rect'});
This line (among with others) are available through an array. Normally I create this shape as follows:
rect = new Kinetic.Rect({
width: 150,
height: 50,
stroke: 'black',
fill: fill,
strokeWidth: .5,
cornerRadius: 25,
name: 'rect'
});
How I create this shape from an array/string?
(rect = new "string from array[xx]")?
KineticJS shapes (like Rect) are defined using a plain javascript object containing your desired properties.
Note: you're fill definition relies on a variable fill. Be sure fill is valid!
// define a rectangle
var rectDefinition1={
width: 150,
height: 50,
stroke: 'black',
strokeWidth: .5,
cornerRadius: 25
};
You can feed this definition into a function like this that creates a Kinetic.Rect from your definition and applies a specific new name to that object.
// function to create a Kinetic.Rect from the rectangle definition
function makeRectFromObject(myObject,newName,newFill){
var rect=new Kinetic.Rect(myObject);
rect.name(newName);
rect.fill(newFill);
return(rect);
}
You would use the creation function like this:
// Use like this
var rect1=makeRectFromObject(rectDefinition1,"rect1","red");
// add the newly created rect to the layer and redisplay the layer
layer.add(rect1);
layer.draw();
If you want to store and retrieve your object definitions, you can use JSON:
// serialize your javascript object to a JSON text string
// This is a regular text string that can be stored in a file or database.
var myRectDefinitionAsJSONText = JSON.stringify(rectDefinition1);
// create a javascript object from your JSON text string
var rectDefinition1= JSON.parse( myRectDefinitionAsJSONText );
I would like to know if it is possible to change the z-index property of the shapes?
I'm making the route above the circle shape but the route remains behind.
What can i do?
You can do this by altering the zIndex property of the Polyline, like all MapObjects, the Polyline is derived from Object which is where the zIndex is defined.
If you have a Polyline like this:
var points = [
new nokia.maps.geo.Coordinate(50.2, 8.57),
new nokia.maps.geo.Coordinate(50.1299, 8.5680),
new nokia.maps.geo.Coordinate(50.0969, 8.4829),
new nokia.maps.geo.Coordinate(50.0819, 8.4856),
new nokia.maps.geo.Coordinate(50.0555, 8.4479),
new nokia.maps.geo.Coordinate(50.02, 8.43)
];
// Purple polyline
poly = new nokia.maps.map.Polyline(
points,
{
pen: {
strokeColor: "#22CA",
lineWidth: 5
}
}
);
And any geoshape (e.g. a Polygon) like this:
gon = new nokia.maps.map.Polygon(
[
new nokia.maps.geo.Coordinate(50.2001, 8.6381),
new nokia.maps.geo.Coordinate(50.2190, 8.5474),
new nokia.maps.geo.Coordinate(50.2001, 8.5337),
new nokia.maps.geo.Coordinate(50.1799, 8.4774),
new nokia.maps.geo.Coordinate(50.1693, 8.4664),
new nokia.maps.geo.Coordinate(50.1522, 8.4657),
new nokia.maps.geo.Coordinate(50.1205, 8.5310),
new nokia.maps.geo.Coordinate(50.1363, 8.5879),
new nokia.maps.geo.Coordinate(50.1882, 8.6168)
],
{
pen: { strokeColor: "#000", lineWidth: 1 },
brush: { color: "#C22A" }
}
);
Then the object that is placed on the map first will be at the back. By default both objects will have a zIndex of zero. If you increase the zIndex as shown:
poly.set("zIndex", 1)
Then it will move on top of all objects with the default zIndex