Merge pull request #12695 from mike-000/KML
Update Google KML icon anchors and correct icon scaling
This commit is contained in:
+72
-28
@@ -9,6 +9,7 @@ import GeometryType from '../geom/GeometryType.js';
|
|||||||
import Icon from '../style/Icon.js';
|
import Icon from '../style/Icon.js';
|
||||||
import IconAnchorUnits from '../style/IconAnchorUnits.js';
|
import IconAnchorUnits from '../style/IconAnchorUnits.js';
|
||||||
import IconOrigin from '../style/IconOrigin.js';
|
import IconOrigin from '../style/IconOrigin.js';
|
||||||
|
import ImageState from '../ImageState.js';
|
||||||
import LineString from '../geom/LineString.js';
|
import LineString from '../geom/LineString.js';
|
||||||
import MultiLineString from '../geom/MultiLineString.js';
|
import MultiLineString from '../geom/MultiLineString.js';
|
||||||
import MultiPoint from '../geom/MultiPoint.js';
|
import MultiPoint from '../geom/MultiPoint.js';
|
||||||
@@ -231,11 +232,6 @@ let DEFAULT_IMAGE_STYLE_SIZE;
|
|||||||
*/
|
*/
|
||||||
let DEFAULT_IMAGE_STYLE_SRC;
|
let DEFAULT_IMAGE_STYLE_SRC;
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {number}
|
|
||||||
*/
|
|
||||||
let DEFAULT_IMAGE_SCALE_MULTIPLIER;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {import("../style/Image.js").default}
|
* @type {import("../style/Image.js").default}
|
||||||
*/
|
*/
|
||||||
@@ -311,6 +307,15 @@ export function getDefaultStyleArray() {
|
|||||||
return DEFAULT_STYLE_ARRAY;
|
return DEFAULT_STYLE_ARRAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function that returns the scale needed to normalize an icon image to 32 pixels.
|
||||||
|
* @param {import("../size.js").Size} size Image size.
|
||||||
|
* @return {number} Scale.
|
||||||
|
*/
|
||||||
|
function scaleForSize(size) {
|
||||||
|
return 32 / Math.min(size[0], size[1]);
|
||||||
|
}
|
||||||
|
|
||||||
function createStyleDefaults() {
|
function createStyleDefaults() {
|
||||||
DEFAULT_COLOR = [255, 255, 255, 1];
|
DEFAULT_COLOR = [255, 255, 255, 1];
|
||||||
|
|
||||||
@@ -318,7 +323,7 @@ function createStyleDefaults() {
|
|||||||
color: DEFAULT_COLOR,
|
color: DEFAULT_COLOR,
|
||||||
});
|
});
|
||||||
|
|
||||||
DEFAULT_IMAGE_STYLE_ANCHOR = [20, 2]; // FIXME maybe [8, 32] ?
|
DEFAULT_IMAGE_STYLE_ANCHOR = [20, 2];
|
||||||
|
|
||||||
DEFAULT_IMAGE_STYLE_ANCHOR_X_UNITS = IconAnchorUnits.PIXELS;
|
DEFAULT_IMAGE_STYLE_ANCHOR_X_UNITS = IconAnchorUnits.PIXELS;
|
||||||
|
|
||||||
@@ -329,8 +334,6 @@ function createStyleDefaults() {
|
|||||||
DEFAULT_IMAGE_STYLE_SRC =
|
DEFAULT_IMAGE_STYLE_SRC =
|
||||||
'https://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png';
|
'https://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png';
|
||||||
|
|
||||||
DEFAULT_IMAGE_SCALE_MULTIPLIER = 0.5;
|
|
||||||
|
|
||||||
DEFAULT_IMAGE_STYLE = new Icon({
|
DEFAULT_IMAGE_STYLE = new Icon({
|
||||||
anchor: DEFAULT_IMAGE_STYLE_ANCHOR,
|
anchor: DEFAULT_IMAGE_STYLE_ANCHOR,
|
||||||
anchorOrigin: IconOrigin.BOTTOM_LEFT,
|
anchorOrigin: IconOrigin.BOTTOM_LEFT,
|
||||||
@@ -338,7 +341,7 @@ function createStyleDefaults() {
|
|||||||
anchorYUnits: DEFAULT_IMAGE_STYLE_ANCHOR_Y_UNITS,
|
anchorYUnits: DEFAULT_IMAGE_STYLE_ANCHOR_Y_UNITS,
|
||||||
crossOrigin: 'anonymous',
|
crossOrigin: 'anonymous',
|
||||||
rotation: 0,
|
rotation: 0,
|
||||||
scale: DEFAULT_IMAGE_SCALE_MULTIPLIER,
|
scale: scaleForSize(DEFAULT_IMAGE_STYLE_SIZE),
|
||||||
size: DEFAULT_IMAGE_STYLE_SIZE,
|
size: DEFAULT_IMAGE_STYLE_SIZE,
|
||||||
src: DEFAULT_IMAGE_STYLE_SRC,
|
src: DEFAULT_IMAGE_STYLE_SRC,
|
||||||
});
|
});
|
||||||
@@ -941,16 +944,14 @@ function createNameStyleFunction(foundStyle, name) {
|
|||||||
let textAlign = 'start';
|
let textAlign = 'start';
|
||||||
const imageStyle = foundStyle.getImage();
|
const imageStyle = foundStyle.getImage();
|
||||||
if (imageStyle) {
|
if (imageStyle) {
|
||||||
let imageSize = imageStyle.getImageSize();
|
const imageSize = imageStyle.getSize();
|
||||||
if (imageSize === null) {
|
if (imageSize && imageSize.length == 2) {
|
||||||
imageSize = DEFAULT_IMAGE_STYLE_SIZE;
|
|
||||||
}
|
|
||||||
if (imageSize.length == 2) {
|
|
||||||
const imageScale = imageStyle.getScaleArray();
|
const imageScale = imageStyle.getScaleArray();
|
||||||
|
const anchor = imageStyle.getAnchor();
|
||||||
// Offset the label to be centered to the right of the icon,
|
// Offset the label to be centered to the right of the icon,
|
||||||
// if there is one.
|
// if there is one.
|
||||||
textOffset[0] = (imageScale[0] * imageSize[0]) / 2;
|
textOffset[0] = imageScale[0] * (imageSize[0] - anchor[0]);
|
||||||
textOffset[1] = (-imageScale[1] * imageSize[1]) / 2;
|
textOffset[1] = imageScale[1] * (imageSize[1] / 2 - anchor[1]);
|
||||||
textAlign = 'left';
|
textAlign = 'left';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1278,14 +1279,21 @@ function iconStyleParser(node, objectStack) {
|
|||||||
anchorXUnits = hotSpot.xunits;
|
anchorXUnits = hotSpot.xunits;
|
||||||
anchorYUnits = hotSpot.yunits;
|
anchorYUnits = hotSpot.yunits;
|
||||||
anchorOrigin = hotSpot.origin;
|
anchorOrigin = hotSpot.origin;
|
||||||
} else if (src === DEFAULT_IMAGE_STYLE_SRC) {
|
|
||||||
anchor = DEFAULT_IMAGE_STYLE_ANCHOR;
|
|
||||||
anchorXUnits = DEFAULT_IMAGE_STYLE_ANCHOR_X_UNITS;
|
|
||||||
anchorYUnits = DEFAULT_IMAGE_STYLE_ANCHOR_Y_UNITS;
|
|
||||||
} else if (/^http:\/\/maps\.(?:google|gstatic)\.com\//.test(src)) {
|
} else if (/^http:\/\/maps\.(?:google|gstatic)\.com\//.test(src)) {
|
||||||
anchor = [0.5, 0];
|
// Google hotspots from https://kml4earth.appspot.com/icons.html#notes
|
||||||
anchorXUnits = IconAnchorUnits.FRACTION;
|
if (/pushpin/.test(src)) {
|
||||||
anchorYUnits = IconAnchorUnits.FRACTION;
|
anchor = DEFAULT_IMAGE_STYLE_ANCHOR;
|
||||||
|
anchorXUnits = DEFAULT_IMAGE_STYLE_ANCHOR_X_UNITS;
|
||||||
|
anchorYUnits = DEFAULT_IMAGE_STYLE_ANCHOR_Y_UNITS;
|
||||||
|
} else if (/arrow-reverse/.test(src)) {
|
||||||
|
anchor = [54, 42];
|
||||||
|
anchorXUnits = DEFAULT_IMAGE_STYLE_ANCHOR_X_UNITS;
|
||||||
|
anchorYUnits = DEFAULT_IMAGE_STYLE_ANCHOR_Y_UNITS;
|
||||||
|
} else if (/paddle/.test(src)) {
|
||||||
|
anchor = [32, 1];
|
||||||
|
anchorXUnits = DEFAULT_IMAGE_STYLE_ANCHOR_X_UNITS;
|
||||||
|
anchorYUnits = DEFAULT_IMAGE_STYLE_ANCHOR_Y_UNITS;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let offset;
|
let offset;
|
||||||
@@ -1308,16 +1316,13 @@ function iconStyleParser(node, objectStack) {
|
|||||||
rotation = toRadians(heading);
|
rotation = toRadians(heading);
|
||||||
}
|
}
|
||||||
|
|
||||||
let scale = /** @type {number|undefined} */ (object['scale']);
|
const scale = /** @type {number|undefined} */ (object['scale']);
|
||||||
|
|
||||||
const color = /** @type {Array<number>|undefined} */ (object['color']);
|
const color = /** @type {Array<number>|undefined} */ (object['color']);
|
||||||
|
|
||||||
if (drawIcon) {
|
if (drawIcon) {
|
||||||
if (src == DEFAULT_IMAGE_STYLE_SRC) {
|
if (src == DEFAULT_IMAGE_STYLE_SRC) {
|
||||||
size = DEFAULT_IMAGE_STYLE_SIZE;
|
size = DEFAULT_IMAGE_STYLE_SIZE;
|
||||||
if (scale === undefined) {
|
|
||||||
scale = DEFAULT_IMAGE_SCALE_MULTIPLIER;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const imageStyle = new Icon({
|
const imageStyle = new Icon({
|
||||||
@@ -1334,6 +1339,37 @@ function iconStyleParser(node, objectStack) {
|
|||||||
src: this.iconUrlFunction_(src),
|
src: this.iconUrlFunction_(src),
|
||||||
color: color,
|
color: color,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const imageScale = imageStyle.getScaleArray()[0];
|
||||||
|
const imageSize = imageStyle.getSize();
|
||||||
|
if (imageSize === null) {
|
||||||
|
const imageState = imageStyle.getImageState();
|
||||||
|
if (imageState === ImageState.IDLE || imageState === ImageState.LOADING) {
|
||||||
|
const listener = function () {
|
||||||
|
const imageState = imageStyle.getImageState();
|
||||||
|
if (
|
||||||
|
!(
|
||||||
|
imageState === ImageState.IDLE ||
|
||||||
|
imageState === ImageState.LOADING
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
const imageSize = imageStyle.getSize();
|
||||||
|
if (imageSize && imageSize.length == 2) {
|
||||||
|
const resizeScale = scaleForSize(imageSize);
|
||||||
|
imageStyle.setScale(imageScale * resizeScale);
|
||||||
|
}
|
||||||
|
imageStyle.unlistenImageChange(listener);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
imageStyle.listenImageChange(listener);
|
||||||
|
if (imageState === ImageState.IDLE) {
|
||||||
|
imageStyle.load();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (imageSize.length == 2) {
|
||||||
|
const resizeScale = scaleForSize(imageSize);
|
||||||
|
imageStyle.setScale(imageScale * resizeScale);
|
||||||
|
}
|
||||||
styleObject['imageStyle'] = imageStyle;
|
styleObject['imageStyle'] = imageStyle;
|
||||||
} else {
|
} else {
|
||||||
// handle the case when we explicitly want to draw no icon.
|
// handle the case when we explicitly want to draw no icon.
|
||||||
@@ -2619,7 +2655,15 @@ function writeIconStyle(node, style, objectStack) {
|
|||||||
|
|
||||||
properties['Icon'] = iconProperties;
|
properties['Icon'] = iconProperties;
|
||||||
|
|
||||||
const scale = style.getScale();
|
let scale = style.getScaleArray()[0];
|
||||||
|
let imageSize = size;
|
||||||
|
if (imageSize === null) {
|
||||||
|
imageSize = DEFAULT_IMAGE_STYLE_SIZE;
|
||||||
|
}
|
||||||
|
if (imageSize.length == 2) {
|
||||||
|
const resizeScale = scaleForSize(imageSize);
|
||||||
|
scale = scale / resizeScale;
|
||||||
|
}
|
||||||
if (scale !== 1) {
|
if (scale !== 1) {
|
||||||
properties['scale'] = scale;
|
properties['scale'] = scale;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2299,6 +2299,56 @@ describe('ol.format.KML', function () {
|
|||||||
expect(style.getZIndex()).to.be(undefined);
|
expect(style.getZIndex()).to.be(undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("can read a feature's IconStyle, load the image and reset the scale", function (done) {
|
||||||
|
format = new KML({
|
||||||
|
iconUrlFunction: function (href) {
|
||||||
|
return href.replace('http://foo/', 'spec/ol/data/');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const text =
|
||||||
|
'<kml xmlns="http://earth.google.com/kml/2.2">' +
|
||||||
|
' <Placemark>' +
|
||||||
|
' <Style>' +
|
||||||
|
' <IconStyle>' +
|
||||||
|
' <Icon>' +
|
||||||
|
' <href>http://foo/dot.png</href>' +
|
||||||
|
' </Icon>' +
|
||||||
|
' </IconStyle>' +
|
||||||
|
' </Style>' +
|
||||||
|
' </Placemark>' +
|
||||||
|
'</kml>';
|
||||||
|
const fs = format.readFeatures(text);
|
||||||
|
expect(fs).to.have.length(1);
|
||||||
|
const f = fs[0];
|
||||||
|
expect(f).to.be.an(Feature);
|
||||||
|
const styleFunction = f.getStyleFunction();
|
||||||
|
expect(styleFunction).not.to.be(undefined);
|
||||||
|
const styleArray = styleFunction(f, 0);
|
||||||
|
expect(styleArray).to.be.an(Array);
|
||||||
|
expect(styleArray).to.have.length(1);
|
||||||
|
const style = styleArray[0];
|
||||||
|
expect(style).to.be.an(Style);
|
||||||
|
expect(style.getFill()).to.be(getDefaultFillStyle());
|
||||||
|
expect(style.getStroke()).to.be(getDefaultStrokeStyle());
|
||||||
|
const imageStyle = style.getImage();
|
||||||
|
expect(imageStyle).to.be.an(Icon);
|
||||||
|
expect(imageStyle.getSrc()).to.eql('spec/ol/data/dot.png');
|
||||||
|
expect(imageStyle.getAnchor()).to.be(null);
|
||||||
|
expect(imageStyle.getOrigin()).to.be(null);
|
||||||
|
expect(imageStyle.getRotation()).to.eql(0);
|
||||||
|
expect(imageStyle.getSize()).to.be(null);
|
||||||
|
expect(imageStyle.getScale()).to.be(1);
|
||||||
|
expect(imageStyle.getImage().crossOrigin).to.eql('anonymous');
|
||||||
|
expect(style.getText()).to.be(getDefaultTextStyle());
|
||||||
|
expect(style.getZIndex()).to.be(undefined);
|
||||||
|
|
||||||
|
setTimeout(function () {
|
||||||
|
expect(imageStyle.getSize()).to.eql([20, 20]);
|
||||||
|
expect(imageStyle.getScale()).to.be(1.6); // 32 / 20
|
||||||
|
done();
|
||||||
|
}, 200);
|
||||||
|
});
|
||||||
|
|
||||||
it("can read a IconStyle's hotspot", function () {
|
it("can read a IconStyle's hotspot", function () {
|
||||||
const text =
|
const text =
|
||||||
'<kml xmlns="http://earth.google.com/kml/2.2">' +
|
'<kml xmlns="http://earth.google.com/kml/2.2">' +
|
||||||
@@ -2446,7 +2496,7 @@ describe('ol.format.KML', function () {
|
|||||||
expect(imageStyle.getAnchor()).to.eql([24, 36]);
|
expect(imageStyle.getAnchor()).to.eql([24, 36]);
|
||||||
expect(imageStyle.getOrigin()).to.eql([24, 108]);
|
expect(imageStyle.getOrigin()).to.eql([24, 108]);
|
||||||
expect(imageStyle.getRotation()).to.eql(0);
|
expect(imageStyle.getRotation()).to.eql(0);
|
||||||
expect(imageStyle.getScale()).to.eql(3.0);
|
expect(imageStyle.getScale()).to.eql(2.0); // 3.0 * 32 / 48
|
||||||
expect(style.getText()).to.be(getDefaultTextStyle());
|
expect(style.getText()).to.be(getDefaultTextStyle());
|
||||||
expect(style.getZIndex()).to.be(undefined);
|
expect(style.getZIndex()).to.be(undefined);
|
||||||
});
|
});
|
||||||
@@ -3027,7 +3077,7 @@ describe('ol.format.KML', function () {
|
|||||||
' <Placemark>' +
|
' <Placemark>' +
|
||||||
' <Style>' +
|
' <Style>' +
|
||||||
' <IconStyle>' +
|
' <IconStyle>' +
|
||||||
' <scale>0.5</scale>' +
|
' <scale>0.75</scale>' + // 0.5 * 48 / 32
|
||||||
' <heading>45</heading>' +
|
' <heading>45</heading>' +
|
||||||
' <Icon>' +
|
' <Icon>' +
|
||||||
' <href>http://foo.png</href>' +
|
' <href>http://foo.png</href>' +
|
||||||
|
|||||||
Reference in New Issue
Block a user