Skip to content

Commit

Permalink
A frame layer sets extent of SVG and makes symbols scale in gui
Browse files Browse the repository at this point in the history
  • Loading branch information
mbloch committed Mar 2, 2024
1 parent bce2953 commit f4c94d6
Show file tree
Hide file tree
Showing 9 changed files with 78 additions and 93 deletions.
4 changes: 2 additions & 2 deletions src/cli/mapshaper-options.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1311,14 +1311,14 @@ export function getOptionParser() {
.validate(V.validateProjOpts);

parser.command('rectangle')
.describe('create a rectangle from a bbox or target layer extent')
.describe('create a rectangle from a bbox or target layer')
.option('bbox', {
describe: 'rectangle coordinates (xmin,ymin,xmax,ymax)',
type: 'bbox'
})
.option('offset', offsetOpt)
.option('width', {
// describe: 'set viewport width in pixels',
describe: 'set width of map in pixels, use rectangle as frame',
type: 'number'
})
.option('aspect-ratio', aspectRatioOpt)
Expand Down
29 changes: 14 additions & 15 deletions src/commands/mapshaper-frame.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -62,18 +62,17 @@ export function getAspectRatioArg(widthArg, heightArg) {
}).reverse().join(',');
}


export function renderFrame(d) {
var lineWidth = 1,
// inset stroke by half of line width
off = lineWidth / 2,
obj = importPolygon([[[off, off], [off, d.height - off],
[d.width - off, d.height - off],
[d.width - off, off], [off, off]]]);
utils.extend(obj.properties, {
fill: 'none',
stroke: d.stroke || 'black',
'stroke-width': d['stroke-width'] || lineWidth
});
return [obj];
}
// export function renderFrame(d) {
// var lineWidth = 1,
// // inset stroke by half of line width
// off = lineWidth / 2,
// obj = importPolygon([[[off, off], [off, d.height - off],
// [d.width - off, d.height - off],
// [d.width - off, off], [off, off]]]);
// utils.extend(obj.properties, {
// fill: 'none',
// stroke: d.stroke || 'black',
// 'stroke-width': d['stroke-width'] || lineWidth
// });
// return [obj];
// }
28 changes: 23 additions & 5 deletions src/commands/mapshaper-rectangle.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import cmd from '../mapshaper-cmd';
import { convertFourSides } from '../geom/mapshaper-units';
import { setDatasetCrsInfo, getDatasetCrsInfo, getCrsInfo } from '../crs/mapshaper-projections';
import { getLayerBounds, layerHasGeometry, setOutputLayerName } from '../dataset/mapshaper-layer-utils';
import {
getLayerBounds,
layerHasGeometry,
setOutputLayerName,
initDataTable,
layerIsRectangle
} from '../dataset/mapshaper-layer-utils';
import { mergeDatasetsIntoDataset } from '../dataset/mapshaper-merging';
import { importGeoJSON } from '../geojson/geojson-import';
import { getPointFeatureBounds } from '../points/mapshaper-point-utils';
Expand Down Expand Up @@ -50,6 +56,13 @@ cmd.rectangles = function(targetLyr, targetDataset, opts) {
// Create rectangles around one or more target layers
//
cmd.rectangle2 = function(target, opts) {
// if target layer is a rectangle and we're applying frame properties,
// turn the target into a frame instead of creating a new rectangle
if (target.layers.length == 1 && opts.width > 0 &&
layerIsRectangle(target.layers[0], target.dataset.arcs)) {
applyFrameProperties(target.layers[0], opts);
return;
}
var datasets = target.layers.map(function(lyr) {
var dataset = cmd.rectangle({layer: lyr, dataset: target.dataset}, opts);
setOutputLayerName(dataset.layers[0], lyr, null, opts);
Expand Down Expand Up @@ -79,16 +92,21 @@ cmd.rectangle = function(source, opts) {
properties: {},
geometry: bboxToPolygon(bounds.toArray(), opts)
};
if (opts.width > 0) {
feature.properties.width = opts.width;
feature.properties.type = 'frame';
}
var dataset = importGeoJSON(feature, {});
applyFrameProperties(dataset.layers[0], opts);
dataset.layers[0].name = opts.name || 'rectangle';
setDatasetCrsInfo(dataset, crsInfo);
return dataset;
};

function applyFrameProperties(lyr, opts) {
if (opts.width > 0 === false) return;
if (!lyr.data) initDataTable(lyr);
var d = lyr.data.getRecords()[0] || {};
d.width = opts.width;
d.type = 'frame';
}

function applyRectangleOptions(bounds, crs, opts) {
var isGeoBox = probablyDecimalDegreeBounds(bounds);
if (opts.offset) {
Expand Down
33 changes: 16 additions & 17 deletions src/furniture/mapshaper-frame-data.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export function getFrameData(dataset, exportOpts) {
var frameLyr = findFrameLayerInDataset(dataset);
var frameData;
if (frameLyr) {
frameData = getFrameLayerData(frameLyr, dataset);
frameData = getFrameLayerData(frameLyr, dataset.arcs);
} else {
frameData = calcFrameData(dataset, exportOpts);
}
Expand All @@ -26,8 +26,8 @@ export function getFrameData(dataset, exportOpts) {
}


function getFrameLayerData(lyr, dataset) {
var bounds = getLayerBounds(lyr, dataset.arcs);
export function getFrameLayerData(lyr, arcs) {
var bounds = getLayerBounds(lyr, arcs);
var d = lyr.data.getReadOnlyRecordAt(0);
var w = d.width || 800;
var h = w * bounds.height() / bounds.width();
Expand All @@ -39,10 +39,6 @@ function getFrameLayerData(lyr, dataset) {
};
}

// export function getFrameLayerData(lyr) {
// return lyr.data && lyr.data.getReadOnlyRecordAt(0);
// }


function calcFrameData(dataset, opts) {
var bounds;
Expand Down Expand Up @@ -76,31 +72,34 @@ export function getFrameSize(bounds, opts) {


// @lyr dataset layer
function isFrameLayer(lyr, dataset) {
export function isFrameLayer(lyr, arcs) {
return getFurnitureLayerType(lyr) == 'frame' &&
layerIsRectangle(lyr, dataset.arcs);
layerIsRectangle(lyr, arcs);
}

export function findFrameLayerInDataset(dataset) {
return utils.find(dataset.layers, function(lyr) {
return isFrameLayer(lyr, dataset);
return isFrameLayer(lyr, dataset.arcs);
});
}

// TODO: handle multiple frames in catalog
export function findFrameDataset(catalog) {
var target = utils.find(catalog.getLayers(), function(o) {
return isFrameLayer(o.layer, o.dataset);
});
return target ? target.dataset : null;
var target = findFrame(catalog);
return target && target.dataset || null;
}

export function findFrameLayer(catalog) {
var target = utils.find(catalog.getLayers(), function(o) {
return isFrameLayer(o.layer, o.dataset);
});
var target = findFrame(catalog);
return target && target.layer || null;
}

export function findFrame(catalog) {
return utils.find(catalog.getLayers(), function(o) {
return isFrameLayer(o.layer, o.dataset.arcs);
});
}

export function getFrameLayerBounds(lyr) {
return new Bounds(getFurnitureLayerData(lyr).bbox);
}
Expand Down
6 changes: 3 additions & 3 deletions src/furniture/mapshaper-furniture.mjs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { stop } from '../utils/mapshaper-logging';
import { renderScalebar } from '../commands/mapshaper-scalebar';
import { renderFrame } from '../commands/mapshaper-frame';
// import { renderFrame } from '../commands/mapshaper-frame';
import { isProjectedCRS } from '../crs/mapshaper-projections';

var furnitureRenderers = {
scalebar: renderScalebar,
frame: renderFrame
scalebar: renderScalebar
// frame: renderFrame
};

// @lyr a layer in a dataset
Expand Down
8 changes: 4 additions & 4 deletions src/gui/gui-map-extent.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export function MapExtent(_position) {
_fullBounds, // full (zoomed-out) content bounds, including any padding
_strictBounds, // full extent must fit inside, if set
_self = this,
_frame;
_frame; // optional frame data (bbox, width, height)

_position.on('resize', function(e) {
if (ready()) {
Expand Down Expand Up @@ -125,16 +125,16 @@ export function MapExtent(_position) {
return this.getTransform().transform(x, y);
};

this.setFrame = function(frame) {
this.setFrameData = function(frame) {
_frame = frame || null;
};

this.getFrame = function() {
this.getFrameData = function() {
return _frame || null;
};

this.getSymbolScale = function() {
if (!_frame) return 0;
if (!_frame) return 1;
var bounds = new Bounds(_frame.bbox);
var bounds2 = bounds.clone().transform(this.getTransform());
return bounds2.width() / _frame.width;
Expand Down
1 change: 1 addition & 0 deletions src/gui/gui-map-nav.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export function MapNav(gui, ext, mouse) {
dragStartEvt,
_fx, _fy; // zoom foci, [0,1]

// Was used in old frame view... remove?
this.setZoomFactor = function(k) {
zoomScaleMultiplier = k || 1;
};
Expand Down
58 changes: 13 additions & 45 deletions src/gui/gui-map.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -190,19 +190,12 @@ export function MshpMap(gui) {
updateVisibleMapLayers();
fullBounds = calcFullBounds();

if (!prevLyr || prevLyr.tabular || _activeLyr.tabular || isFrameView()) {
if (!prevLyr || prevLyr.tabular || _activeLyr.tabular) {
needReset = true;
} else {
needReset = mapNeedsReset(fullBounds, _ext.getFullBounds(), _ext.getBounds(), e.flags);
}

if (isFrameView()) {
_nav.setZoomFactor(0.05); // slow zooming way down to allow fine-tuning frame placement // 0.03
_ext.setFrame(calcFullBounds()); // TODO: remove redundancy with drawLayers()
needReset = true; // snap to frame extent
} else {
_nav.setZoomFactor(1);
}
_ext.setFullBounds(fullBounds, getStrictBounds()); // update 'home' button extent

if (needReset) {
Expand Down Expand Up @@ -232,9 +225,6 @@ export function MshpMap(gui) {
_ext.on('change', function(e) {
if (_basemap) _basemap.refresh(); // keep basemap synced up (if enabled)
if (e.reset) return; // don't need to redraw map here if extent has been reset
if (isFrameView()) {
updateFrameExtent();
}
drawLayers('nav');
});

Expand Down Expand Up @@ -264,21 +254,6 @@ export function MshpMap(gui) {
};
}

// Update map frame after user navigates the map in frame edit mode
function updateFrameExtent() {
var frameLyr = internal.findFrameLayer(model);
var rec = frameLyr.data.getRecordAt(0);
var viewBounds = _ext.getBounds();
var w = viewBounds.width() * rec.width / _ext.width();
var h = w * rec.height / rec.width;
var cx = viewBounds.centerX();
var cy = viewBounds.centerY();
rec.bbox = [cx - w/2, cy - h/2, cx + w/2, cy + h/2];
_ext.setFrame(getFrameData());
_ext.setFullBounds(new Bounds(rec.bbox));
_ext.reset();
}

function getStrictBounds() {
if (internal.isWebMercator(map.getDisplayCRS())) {
return getMapboxBounds();
Expand All @@ -305,9 +280,6 @@ export function MshpMap(gui) {
}

function calcFullBounds() {
if (isPreviewView()) {
return internal.getFrameLayerBounds(internal.findFrameLayer(model));
}
var b = getVisibleContentBounds();

// add margin
Expand Down Expand Up @@ -338,25 +310,21 @@ export function MshpMap(gui) {
return !isPreviewView() && !!_activeLyr.tabular;
}

// Preview view: a special view centering the map 'frame'
// (disabling this pending interface rethink)
function isPreviewView() {
return false;
// var frameLyr = internal.findFrameLayer(model);
// return !!frameLyr; // && isVisibleLayer(frameLyr)
function findFrameLayer() {
return getVisibleMapLayers().find(function(lyr) {
return internal.isFrameLayer(lyr.layer, lyr.arcs);
});
}

// Frame view means frame layer is visible and active (selected)
// (disabling pending rethink)
function isFrameView() {
return false;
// var frameLyr = internal.findFrameLayer(model);
// return isActiveLayer(frameLyr) && isVisibleLayer(frameLyr);
// Preview view: symbols are scaled based on display size of frame layer
function isPreviewView() {
var data = getFrameData();
return !!data;
}

function getFrameData() {
var frameLyr = internal.findFrameLayer(model);
return frameLyr && internal.getFurnitureLayerData(frameLyr) || null;
var lyr = findFrameLayer();
return lyr && internal.getFrameLayerData(lyr.layer, lyr.arcs) || null;
}

function clearAllDisplayArcs() {
Expand Down Expand Up @@ -446,15 +414,15 @@ export function MshpMap(gui) {
var layersMayHaveChanged = action != 'nav'; // !action;
var fullBounds;
var contentLayers = getDrawableContentLayers();
var furnitureLayers = getDrawableFurnitureLayers();
// var furnitureLayers = getDrawableFurnitureLayers();
if (!(_ext.width() > 0 && _ext.height() > 0)) {
// TODO: track down source of these errors
console.error("Collapsed map container, unable to draw.");
return;
}
if (layersMayHaveChanged) {
// kludge to handle layer visibility toggling
_ext.setFrame(isPreviewView() ? getFrameData() : null);
_ext.setFrameData(isPreviewView() ? getFrameData() : null);
updateFullBounds();
updateLayerStyles(contentLayers);
updateLayerStackOrder(model.getLayers());// update menu_order property of all layers
Expand Down
4 changes: 2 additions & 2 deletions src/gui/gui-svg-furniture.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { El } from './gui-el';

function getSvgFurnitureTransform(ext) {
var scale = ext.getSymbolScale();
var frame = ext.getFrame();
var frame = ext.getFrameData();
var p = ext.translateCoords(frame.bbox[0], frame.bbox[3]);
return internal.svg.getTransform(p, scale);
}
Expand All @@ -14,7 +14,7 @@ export function repositionFurniture(container, layer, ext) {
}

export function renderFurniture(lyr, ext) {
var frame = ext.getFrame(); // frame should be set if we're rendering a furniture layer
var frame = ext.getFrameData(); // frame should be set if we're rendering a furniture layer
var obj = internal.getEmptyLayerForSVG(lyr, {});
if (!frame) {
stop('Missing map frame data');
Expand Down

0 comments on commit f4c94d6

Please sign in to comment.