Skip to content

Conversation

@nakul-py
Copy link
Contributor

@nakul-py nakul-py commented Aug 18, 2025

Description

This PR adds opacity as a generic property of all JGIS layers, defined once in the schema packages/schema/src/schema/project/jgis.json.

Now opacity is the exact same type of property as visible

Screencast.From.2025-08-19.21-28-23.mp4

Closes #829

Checklist

  • PR has a descriptive title and content.
  • PR description contains references to any issues the PR resolves, e.g. Resolves #XXX.
  • PR has one of the labels: documentation, bug, enhancement, feature, maintenance
  • Checks are passing.
    Failing lint checks can be resolved with:
    • pre-commit run --all-files
    • jlpm run lint

📚 Documentation preview: https://jupytergis--879.org.readthedocs.build/en/879/
💡 JupyterLite preview: https://jupytergis--879.org.readthedocs.build/en/879/lite

@github-actions
Copy link
Contributor

Binder 👈 Launch a Binder on branch nakul-py/jupytergis/fix%23829

@github-actions
Copy link
Contributor

github-actions bot commented Aug 18, 2025

Integration tests report: appsharing.space

@arjxn-py arjxn-py added the enhancement New feature or request label Aug 18, 2025
@nakul-py nakul-py marked this pull request as draft August 19, 2025 04:00
@nakul-py nakul-py marked this pull request as ready for review August 19, 2025 16:44
@nakul-py
Copy link
Contributor Author

nakul-py commented Aug 20, 2025

bot please update snapshots!!!

Copy link
Member

@martinRenou martinRenou left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for starting this!

Since this is a big schema change, we should probably bump the schema version and have some backward compatibility here https://github.com/geojupyter/jupytergis/blob/main/python/jupytergis_core/jupytergis_core/jgis_ydoc.py#L56

Something like: if the schema version is the old one (when opacity was in the layer parameters) then patch valueDict['layers'] there accordingly by moving the opacity attribute out of the parameters.

This will allow us to continue opening the old file format without error.

@nakul-py nakul-py requested a review from martinRenou August 26, 2025 15:25
Comment on lines +64 to +65
if file_version < Version("0.8.1"):
for _layer_id, layer in valueDict.get("layers", {}).items():
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@martinRenou currently we are using the latest JupyterGIS version 0.8.1 to update the schema. But I am wondering if anyone has an old version, how could we handle that situation? I also use SCHEMA_VERSION instead of latest, but it didn't work.

Copy link
Member

@mfisher87 mfisher87 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your work on this, Nakul! Haven't had time to go deep on this yet, but some questions immediately came to mind. My biggest thought right now is that we should push as much of this as possible into the sharedModel API, as it seems to not be robust enough to support what we're trying to do. Specifically, I think we need the ability to deep-merge state updates (and a DeepPartial generic type to support that).

i.e.

  // new update method
  updateObject(
    id: string,
    value: DeepPartial<IJGISLayer> | DeepPartial<IJGISSource>,
  ) { 
    // DEEP merge `value` with the current state of object.
  }

// ...

// updated call would _only_ change the specified properties
this.props.model.sharedModel.updateObject(myLayerId, {
  opacity: true,
  parameters: {
    symbologyState: {
      colorRamp: "viridis"
    }
  },
});

(or, maybe better, use the updateLayer, updateSource methods for this)

How do you all feel about this? @martinRenou @arjxn-py @gjmooney more thoughts on this in a comment below.

layerSchema['required'] = ['name', ...layerSchema['required']];
layerSchema['properties'] = {
name: { type: 'string', description: 'The name of the layer' },
opacity: {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this? It should already be part of the object, right?

Copy link
Contributor Author

@nakul-py nakul-py Sep 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this?

I think we need.

maximum: 1,
},
};
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can omit this since it's defined in the jsonSchema, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we also need this because if we remove this, then how would we add this to layerschema?
@mfisher87 if you have any thoughts about how to add opacity to formschemaregistry, you can share :)

source: sourceId,
},
visible: true,
opacity: 1.0,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want, we can omit this since the default is 1

this.props.layer,
params,
);
}
Copy link
Member

@mfisher87 mfisher87 Aug 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fact that a change like this is necessary makes me wonder if we're on the wrong path (I'm not saying your approach is wrong, more that our architecture needs work ;) ) with this change. We have an API for updating layer/source parameters, and moving opacity out of the parameters key is causing us to work around that API. That makes this change much more extensive than I expected. Perhaps the right path here is to update the sharedModel API to support more selective updates.

Perhaps the updateObjectParameters method could be updated to look like this?

  updateObject(
    id: string,
    value: Partial<IJGISLayer> | Partial<IJGISSource>,
  ) { 
    // DEEP merge `value` with the current state of object.
  }

Perhaps it's making our life a bit more complicated to have updateObjectParameters capable of acting on layers or sources, and we should instead have updateLayer, updateSource, etc. (these already exist, but they require you to pass a full layer object in, and replace it in the model) as the sole API method for doing updates of these nested objects?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mfisher87 I let you have a look at 6459f70 (#879) if it aligns with your idea

@nakul-py nakul-py requested a review from mfisher87 September 10, 2025 10:01
@arjxn-py
Copy link
Member

Bot please update snapshots

@arjxn-py arjxn-py closed this Sep 11, 2025
@arjxn-py arjxn-py reopened this Sep 11, 2025
Copy link
Member

@arjxn-py arjxn-py left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking good now.
Will wait for @martinRenou & @mfisher87 to merge

@arjxn-py
Copy link
Member

@nakul-py can you try to manually update the conflicting snapshots? Then we can merge this

@mfisher87
Copy link
Member

@arjxn-py any thoughts on this idea? #879 (comment)

@nakul-py
Copy link
Contributor Author

Hey @mfisher87 , I already implemented your idea in this commit 6459f70.

layer.parameters = {
...layer.parameters,
...value,
const layerValue = value as Partial<IJGISLayer>;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to use a typeguard here instead of a cast? I suppose the only way to do that would be to look at the structure of the object, and that may not make sense / be readable.

Copy link
Contributor Author

@nakul-py nakul-py Nov 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is not any reliable way to use a type guard here because Partial<IJGISLayer> and Partial<IJGISSource> don’t have any guaranteed structural differences. Both interfaces share name, type, and parameters, and the fields that do differ (visible, opacity, filters) are optional. That means a structural type guard would either be unreliable or too hard to maintain.

@mfisher87
Copy link
Member

mfisher87 commented Nov 26, 2025

Hey @mfisher87 , I already implemented your idea in this commit 6459f70.

Oh, nice! Sorry I didn't check the source code first :D

Though this is a deep change, and I'm wondering if we have consensus that it's a good idea? @martinRenou @arjxn-py ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

Move layer opacity one level up in the project file

4 participants